Archive for the ‘Javascript’ Category

Compactar com GZip e armazenar em Cache arquivos .js e .css (solução)

Thursday, October 25th, 2007

Editado:

Pessoal, para a melhor solução para este tipo de problema vejam a regra número 4 deste post.
Se quizer entender mais sobre GZip continue lendo.

Fim da edição.

GZip é uma compactação feita pelo servidor web antes de enviar os arquivos para o browser, mas para que isso ocorra com sucesso deve-se mudar o cabeçalho dos arquivos para indicar que o arquivo aceita esse tipo de compactação, para que o servidor compacte este arquivo e quando esse arquivo chegar no browser, este descompacte-o, tornando-o um arquivo normal como todos os outros novamente.

A motivo de informação sobre os benefícios da compactação GZip darei um exemplo de uma biblioteca muito famosa, a JQuery, que tem em torno de 77kb em sua forma normal. Se for usado o compactador de javascript do Yahoo! ele passa a ter em torno de 46kb, e se for usada a compactação GZip em cima desse arquivo a biblioteca fica com incríveis 15kb!

Vinha a um tempo pensando em uma forma inteligente de compactar arquivos “.js” e “.css” usando GZip. Li vários artigos no Yahoo! e em fórums diversos e não encontrei nada falando a respeito, e quando falavam não era de forma clara, tanto é que só fui ver uma forma inteligente de se fazer isso a uns minutos atrás, quando achei algo a respeito no site do Joomla. É uma extensão que modifica o cabeçalho dos arquivos que são incluídos nas páginas desse sistema. Vendo a extensão eu pude ver o quão simples é modificar o cabeçalho de arquivos .css e .js de forma inteligente. Antes havia pensado em modificar a extensão dos arquivos para “.php” e fazer essas modificações (trabalho de corno), vi que funcionou mas não era viável em um projeto grande.

Bom, falei demais já. Vamos para o código da solução:

  /**
* @author Fábio Miranda Costa  
* Usado para compactar arquivos JS e CSS, acrescenta-os ao cache do browser
* e faz algumas melhorias adicionais. 
*/
$file = $_GET[”file”];
$ext = substr($file,strrpos($file, “.”)+1,strlen($file));
ob_start (”ob_gzhandler”);
if($ext==”js”) $ext=”javascript”;
header( “Content-type: text/”.$ext.”; charset: iso-8859-1“);//não se esqueça de mudar para o charset que você usa
header( “Content-Encoding: gzip,deflate”);
header( “Expires: “.gmdate(”D, d M Y H:i:s”, time() + (24 * 60 * 60)) . ” GMT”);//adiciona 1 dia ao tempo de expiração
header( “ETag: “);//a idéia é apagar o conteúdo da Etag, ver post http://www.meiocodigo.com/2007/12/21/melhorando-o-tempo-de-carregamento-de-um-site/
header( “Cache-Control: must-revalidate, proxy-revalidate” );
include($file);
ob_flush();

Fiz esse código o mais compacto possível, já que será usado em todos os includes de css e js do site.
Não vou explicar muito como funciona, apenas como usá-lo, que é o que importa.
Para usar é muito fácil, apenas crie um arquivos incluindo o código acima, no meu exemplo o arquivo é “file_inc.php”, e coloque no cabeçalho do seu site os includes de arquivos css e javascript seguindo os exemplos abaixo:

para javascript:

<script src="incs/file_inc.php?file=funcs.js" type="text/javascript"></script>

No atributo “src” coloque o caminho para o arquivo “file_inc.php” depois passe a variável “file” usando GET, ou seja adicione o texto “?file=” logo após o nome do arquivo, como pode-se ver no exemplo e depois coloque o endereço do arquivo “.js” que você quer incluir em seu site. Lembrando que esse endereço será sempre relativo ao arquivos file_inc.php, ou seja, se o “file_inc.php” estiver na pasta “inc/includer/” e o arquivo que você quer incluir está na pasta “inc/js/” o “src” do include ficará “inc/includer/file_inc.php?file=../js/funcs.js”.

para css:

<link href="incs/file_inc.php?file=styles.css" rel="stylesheet" type="text/css" />

Com o css é a mesma coisa, o que muda é apenas o óbvio, ou seja, a tag será “link” e o atributo usado passará a ser “href”.

Solução para update de “select” usando Ajax no IE

Monday, August 27th, 2007

Hoje, depois de um certo tempo sem postar, irei apresentar uma solução para o problema do update em tags “select” no IE, mas antes explicarei que problema é esse para quem não entendeu.
O Internet Explorer (IE) não aceita, por um bug, a inserção de nós “option” em um nó “select” usando a propriedade inneHTML do mesmo. Ao se tentar isso as opções do “select” simplesmente não aparecem, ficam todas brancas, é tosco!
por exemplo, se eu tentar executar o código abaixo as options não aparecerão no IE:

document.getElementById("id_de_um_select").innerHTML = "Opção 1";

Agora vou mostrar a solução para esse problema, ela é bem simples de entender e o método é bastante rápido e eficiente, eu mesmo que fiz depois de penar com esse bug e testar alguns métodos que encontrei por ai na net e ver que quando eles eram usados com uma lista muito grande de “options” o “select” demorava anos para ser atualizado. Nesse caso não o método funciona mais ou menos na mesma velocidade para lista de tamanhos diferentes. Eis o método, e logo abaixo a explicação do mesmo:


/**
 * Método usado para jogar conteúdo no innnerHTML de selects.
 * @param {Object} selectObj - o Select que receberá o texto.
 * @param {Object} texto -  o texto que será incorporado no objeto select.
 */
function updateSelect(selectObj,texto){
	var tempDiv = document.createElement("div");
	var attString = "";
	var att = selectObj.attributes;
	for(var i = 0 ; i < att.length ; i++)
		if(att[i].nodeValue != "" && att[i].nodeValue != null )//o IE diz que nó tem todos os atributos possíveis com valor null ou "" então temos que filtrar esses valores
			tempDiv.innerHTML = "<select "+attString+">"+texto+"</select>";
	var newSelect = tempDiv.firstChild;
	selectObj.parentNode.replaceChild(newSelect,selectObj);
	selectObj = null;tempDiv = null;
	return newSelect;
}

Como podemos ver essa função tem dois parâmetros, “selectObj” e “texto”. selectObj é o objeto select que receberá a string “texto” em seu “innerHTML”.
Bom vou pular as partes bestas…
No único “for” dessa função o que estou fazendo é jogando todos os atributos do nó “select” numa string para futuramente colocar no innerHTML de uma div temporário que é criada. Dentro da tag “select” eu coloco o parâmetro “texto” e jogo eles no innerHTML da div temporária, depois crio um select temporário que recebe o primeiro nó da div temporária (no caso é o nosso select) e substituo o select anterior por este novo.
Simples não?

Vou anexar a este post a biblioteca “max”, que foi apresentada anteriormente em outro post, já com esse novo método sendo usado automaticamente ao se tentar fazer um update em um nó “select”.
Em breve estarei postando mais um método para a biblioteca “max” que vai torná-la capaz de ser usada com o method “post”, normalmente usado em formulários.
Qualquer dúvida em como usar a biblioteca por favor me avisem! e seu comentário é muito importante!!! :D

Arquivos:

Biblioteca AJAX

Wednesday, May 23rd, 2007

Bem… Começarei os posts sobre AJAX com esse “esqueleto” de uma biblioteca, orientada a objeto, para aplicações em AJAX.
O código abaixo deve ser colocado em um arquivo “max_ajax.js” ou outro nome de sua escolha. Mas no final desse comentário eu anexei os 3 arquivos citados aqui neste post.

/**
 * @author Fábio Miranda Costa - www.meiocodigo.com
 * @version 0.26
 * Pequena biblioteca para uso geral em Ajax.
 * bugs conhecidos: Não conversa muito bem com tabelas.
 * Testado com IE 6 e firefox 2.
 * Qualquer dúvida mande e-mail para meiocodigo@gmail.com ou simplesmente comente.
 */

if(typeof window.max === "undefined") var max = new Object();

Esse inicio apenas verifica a existência do objeto max antes de criá-lo para ser possível acrescentar mais de uma das bibliotecas max.
Aos poucos eu irei postando outras bibliotecas que eu fiz para não ficar com muito conteúdo em um só post.

Métodos

Construtor

/**
 * Construtor da classe max.Ajax
 *
 * @param {String} url - a url do arquivo que receberá a chamada ajax
 * @param {Object} options - um objeto que conterá as possíveis opções, entre elas estão:
 * update (opcional) - um id de um nó DOM que receberá a resposta do servidor.
 * onComplete (opcional) - função que será executada logo após o servidor ter respondido ao pedido.
 *
 */
max.Ajax = function(url,options){

Este é o método construtor da classe max.Ajax. Como ele podemos inicializar as variáveis usadas internamente em um objeto desta classe.
Se você quizer aprender alguma coisa sobre Javascript e orientação a objeto vá para o site www.w3schools.com que é um site excelente para iniciantes, aprendi muita coisa lá no inicio.

Bem voltando ao código inicialmente temos a declaração do construtor como sendo uma função que faz as seguintes coisas:

this.xmlHttp = this.createXMLHttp();//criação do objeto que tratará da nossa requisição ao servidor

Cria um XMLHttpObject para fazer as requisições assincronamente ao servidor.

this.url = url;

Inicializa a variável url pertencente a classe com o primeiro valor passado por parâmetro na inicialização do objeto. Este valor deve ser a url para o arquivo que deve ser enviada a requisição Ajax.

this.update = options.update || null;//se não for definida um id para fazer o update, this.update = null, caso contrário this.update = options.update

Inicializa a variável update pertencente a classe com o valor de options.update se esta for declarada ou inicializa ela com null se não tiver sido declarada. Este valor deve ser o id de algum nó DOM no seu HTML, este nó receberá o conteúdo que foi retornado pelo arquivo que recebeu a requisição Ajax. (complicado? :S)

this.onComplete = options.onComplete || function(){};//se tiver sido definida uma função para ser executado ao final da requisição então this.onComplete = options.onComplete senão this.onComplete = null;
};

Inicializa a variável onComplete pertencente a classe com o valor de option.onComplete, que deve ser sempre uma função. A função que for passada por parâmetro nesse caso será executada logo após a requisição Ajax ser finalizada.

Requisição Ajax por GET

max.Ajax.prototype = Object({
	/**
	 * Método para enviar pedido get.
	 */
	get:
		function(){

Este é o método de envio de uma requisição Ajax usando GET.

var thisObj = this;//fazendo referência a este objeto

Inicializamos a variável thisObj com o valor “this”.
Você deve estar pensando: “que babaca… pra que isso?”. Calma eu vou explicar depois por que inicializei essa variável dessa forma.

this.xmlHttp.onreadystatechange = function(){thisObj.updateFunc();};//esta linha define qual será a função que será executada quando nosso objeto xmlHttp tiver seu estado modificado, no caso updateFunc()

Aqui nós definimos um método ( “updateFunc()” ) que será executado toda vez que o estado do nosso XMLHttpObject mudar. Existem no máximo 5 possíveis valores para o estado, dependendo do browser.

  • Estado 0 - A requisição assíncrona com o servidor não foi iniciada
  • Estado 1 - A requisição foi inicializada
  • Estado 2 - A requisição foi enviada
  • Estado 3 - A requisição está sendo processsada
  • Estado 4 - A requisição foi completada, com sucesso ou não.

Agora vem a explicação do por que inicializar a variável thisObj com “this”. Se eu tivesse feito essa mesma linha dessa forma:

this.xmlHttp.onreadystatechange = function(){this.updateFunc();};

vocês acham que funcionaria?
A resposta é não, por que o “this” neste caso está se referindo ao “this.xmlHttp” por estar dentro de seu escopo, por isso eu fiz uma atribuição ao max.Ajax (var thisObj = this;) e usei ele dentro do escopo do “this.xmlHttp”.

this.url += (this.url.indexOf("?") === -1)?"?":"&";
this.url += "ridwes="+Math.random();//adicionando uma variável randomica ao nosso get para que resolva um problema de cache que acredito que só aconteça no IE, chamei de ridwes por que acredito que ninguem teria uma variavel com esse nome, mas se for o caso...modifique hehe

Essas duas linhas acrescentam uma variável “ridwes” à nossa requisição. Esse nome eu escolhi arbitrariamente achando que ninguem escolheria tal nome para uma variável mas se você tiver escolhido então modifique o nome de sua variável ou modifique o código.
O objetivo de adicionar essa variável com valor randômico é de evitar problemas de cache que acontecem freqüentemente no IE.

this.xmlHttp.open("get",this.url,true);//define que o método usado para a nossa requisição será o 'get', a url do arquivo que receberá a requisição e o true indica que a chamada será assincrona.

Aqui temos, como o nome diz, a abertura de uma conexão com o servidor. O primeiro parâmetro da função open é o método utilizado para a comunicação (”get” ou “post”), o segundo é a url para o arquivo que receberá a chamada do browser, podem ser incluidas variáveis juntamente com ela, e o terceiro é um valor booleano que indica se a chamada será assíncrona (true) ou síncrona (false).

this.xmlHttp.send(null);//finalmente envia nosso pedido
},

E finalmente enviamos a chamada ao servidor, passando null como parametro. Veremos futuramente que se tivessemos usando o método “post” as variáveis seriam passadas por parâmetro ao invez do null.

Update nó DOM

	/**
	 * Método executado toda vez que o xmlHttp mudar de estado.
	 */
	updateFunc:
		function(){

Esta função, como já foi visto neste mesmo post, é executada no evento “readystatechange” do nosso objeto xmlHttp.

if (this.xmlHttp.readyState==4 || this.xmlHttp.readyState=="complete"){//verifica se o estado do xmlHttp é de completo, ou seja se a resposta do servidor já está "em mãos"
	if (this.xmlHttp.status == 200){//verifica se o estatus do objeto é de sucesso, caso contrario o ideal seria mostrar um erro, mostrarei isso no futuro
		if (this.update){
			document.getElementById(this.update).innerHTML = this.xmlHttp.responseText;//joga o texto de resposta do servidor dentro do nó DOM especificado na opção update, se ela existir
		}
		this.onComplete(this.xmlHttp.responseText,this.xmlHttp.responseXML);//executa a função ao final da chamada ajax, também chamada de callback
	}
}
},

Resolvi comentar todas essas linhas juntas porque são muitos “ifs”, um dentro do outro, pode acabar confundindo alguem.
A primeira linha é uma condição que verifica se a propriedade “readyState” do objeto xmlHttp está em seu estado final, ou seja, que a requisição ao servidor já retornou.
A segunda linha verifica se a propriedade estatus, do mesmo objeto xmlHttp, indica sucesso (o número 200 quer dizer que o servidor não retornou nenhuma mensagem de erro, no futuro veremos outros números e o que cada um deles representa).
A terceira linha verifica se foi passado por parâmetro algum id ao se criar um objeto max.Ajax, se tiver passado, na quarta linha o nó DOM com esse id receberá o responseText, ou seja, o texto de resposta do arquivo que chamamos em nossa requisição Ajax.
E finalmente na sexta linha temos a execução do método que foi passado por parâmetro no onComplete.
Se você não estiver entendendo muito bem veja o exemplo logo no final desse post que irá esclarecer melhor como essa biblioteca funciona.

Criação de objeto XMLHttp

	/**
	 * Copiado na cara de pau, peguei de um artigo no Wrox.
	 * Método usado para a criação do objeto XMLHttp, peça chave para o ajax.
	 * Não explicarei em detalhes.
	 */
	createXMLHttp:
		function(){
			if(typeof XMLHttpRequest != "undefined")//entra em todos os navegadores menos o IE
		        return new XMLHttpRequest();
			else if (window.ActiveXObject) {//no caso do IE
		      var aVersions = [ "MSXML2.XMLHttp.5.0","MSXML2.XMLHttp.4.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp","Microsoft.XMLHttp"];
		      //por cima, o que ela faz é verificar qual a versão mais recente do xmlHttp Object suportada pelo navegador e retorna o objeto
			  for (var i = 0; i < aVersions.length; i++) {
		        try{
		            var oXmlHttp = new ActiveXObject(aVersions[i]);
		            return oXmlHttp;
		        }catch (oError){}
		      }
		    }
			throw new Error("Objeto XMLHttp não pode ser criado.");
		}
});

Não falarei dessa função em detalhes, apenas coloquei alguns comentários aí no código. Mas basicamente essa função cria o objeto xmlHttp.

O código já está com comentários nas linhas mais importantes.
Criei um arquivo para testar a biblioteca com todas as opções possíveis.
Você pode copiar este texto em um arquivo HTML qualquer ou simplesmente baixar os arquivos anexados ao post.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
<script type="text/javascript" src="max_ajax_ref_blog.js" ></script>
<script type="text/javascript">
function testeAjax(thisObj){

var url = "teste.php";//criando uma string cujo valor corresponde à url do arquivo que receberá a requisição Ajax
url += "?valor_teste="+thisObj.innerHTML;//adicionando a variável "valor_teste" à url

var maxAjaxObj = new max.Ajax(url,{update:"teste",onComplete:
function(texto,xml){
alert("O innerHTML é:"+texto);
}
});//aqui estamos criando um objeto max.Ajax passando ao construtor como parâmetros uma url e um objeto com uma propriedade "update"
//cujo valor é o id "teste" indicando que o nó com id="teste" receberá o texto de resposta do servidor, depois temos o onComplete
//que é uma função e será executada logo após a resposta com sucesso do servidor
maxAjaxObj.get();//execução do método get para a nossa chamada Ajax.
}
window.onload = function(){//as função que estão dentro desse escopo só são executadas após todo o carregamento do site
document.getElementById("link_teste").onclick = function(){
		testeAjax(this);
		return false;
	};//aqui nós estamos dizendo que o nó com id="link_teste" escutará esta função no evento "onclick"
}

</script>
</head>

<body>
<a id="link_teste" href="#">Texto para testar :D</a>
<div id="teste"></div>
</body>
</html>

O que esse código faz é enviar um pedido para o arquivo “teste.php” passando por get as variáveis ridwes com valor randomico (explicado no código) e a variável valor_teste que terá como valor a string “Texto para testar :D”.

meu arquivo teste.php ficou:

<?php
echo $_GET["valor_teste"];
?>

ou seja a resposta do servidor será exatamente o valor da variável “valor_teste” que é “Texto para testar :D”.

Traduzindo tudo, quando clicamos no link a requisição ao servidor é enviada quando o browser recebe a resposta da requisição um alerta é mostrado com o texto: “O innerHTML é: Texto para testar :D” e a div de id “teste” receberá também o mesmo texto de reposta do servidor (”Texto para testar :D”) por que eu coloquei como uma de minhas opções update:”teste”.

Espero ter sido claro =/

Qualquer dúvida comente por favor!! :D

Arquivos: