Category Archives: JavaScript

Bridge para encapsular o Cross Browser

No rastro do meu último post, aproveitando a deixa de detecção otimizada trabalhando junto com o Design Pattern Bridge, aproveitaremos esse conceito para encapsularmos a complexidade de se trabalhar com códigos que tenham que rodar em múltiplos Browsers.

O padrão Bridge define o desacoplamento da abstração (interface) com suas implementações e cada implementação possa variar independentemente.

No código passado vimos isso implementado na função addEvent como segue:

var addEvent = function(el, type, fn) {
    el['on'+type] = fn;
};
if(document.addEventListener) {
    addEvent = function(el, type, fn) {
        el.addEventListener(type, fn, false);
    };
} else if(document.attachEvent) {
    addEvent = function(el, type, fn) {
        el.attachEvent('on'+type, fn);
    };
}

Nesse código o método addEvent é uma forma genérica de adicionar eventos a um elemento DOM na página e encapsula o comportamento implementado pelos Browsers. Essa estratégia pode e deve ser adotada em todo o código que necessite de implementação diferente dependendo do navegador, o chamado Cross Browser.

Cross Browser é a técnica de implementar uma construção que rode em múltiplos navegadores sem diferença perceptível ao usuário. O custo de aplicar essa técnica é diminuir a performance da aplicação como um todo, aumentar a complexidade do código mantido, propiciar um ambiente mais sujeito a falhas de implementação e cair em bugs do próprio navegador (como o Memory-Leak no IE).

Instanciar o objeto XMLHttpRequest é outra situação que possui diferença entre o líder de mercado e os demais navegadores. Aplicando essa técnica, faríamos o seguinte código:

var getXHR = function() {
	this.http = new XMLHttpRequest;
	return this.http;
}
var isIE = !!document.attachEvent;
if(isIE) {
      var msxml = [
        'MSXML2.XMLHTTP.3.0',
        'MSXML2.XMLHTTP',
        'Microsoft.XMLHTTP'
      ];
      for (var i=0, len = msxml.length; i < len; ++i) {
        try {
          http = new ActiveXObject(msxml[i]);
          //cria nova implementação
          getXHR = function() {
            return new ActiveXObject(msxml[i]);
          };
          break;
        }
        catch(e) {}
      }
};

Dei a dica no artigo passado de criar um arquivo para cada implementação de navegador diferente e aplicar o conceito de Lazy Loading para carregar sob demanda.

Typically chemist’s shop can sale to you with discreet treatments for various heartiness problems. There are numerous of safe online pharmacies that will deliver medications to your address. There are divers medicines for each afflictions. Learn more about “viagra manufacturer coupon“. Maybe “viagra discount coupons” is a much complicated problem. Matters, like “coupons for viagra“, are coupled numerous types of health problems. If you need to take prescription medications, ask your pharmacist to check your testosterone levels before. Sometimes the treatment options may switch on erectile disfunction remedies or a suction device that helps get an erection. Keep in mind web-site which is ready to sell erectile malfunction drugs like Viagra without a formula is fraudulent. When you purchase from an unknown web-site, you run the risk of getting counterfeit remedies.

Detecção otimizada com javascript

Uma dica interessante para melhorar a otimização do seu código javascript é realizar a detecção no início da interpretação, para isso montar o código de encapsulamento de acordo com o Browser.

Geralmente quando queremos encapsular uma funcionalidade, por diferença de comportamento dos Browsers, testamos em qual Browser o código está sendo verificado e só então definimos o corpo do código. Algo como:

var addEvent = function(el, type, fn) {
    if(document.addEventListener) {
        el.addEventListener(type, fn, false);
    } else if(document.attachEvent) {
        el.attachEvent('on'+type, fn);
    } else {
        el['on'+type] = fn;
    }
}

Observe que sempre na chamada da function addEvent, testamos em qual Browser o código está sendo executado e então utilizamos a forma apropriada com a qual o Browser trabalha.

Imagine que esse tipo de construção, na medida que vai aumentando a complexidade da aplicação, onera a performance em geral, porque sempre terá que executar operações alheias ao objetivo do negócio, como detectar qual o Browser ou construir a function em tempo de execução.

Uma forma de evitar essa construção é uma estrutura como a que segue:

var addEvent = function(el, type, fn) {
    el['on'+type] = fn;
};
if(document.addEventListener) {
    addEvent = function(el, type, fn) {
        el.addEventListener(type, fn, false);
    };
} else if(document.attachEvent) {
    addEvent = function(el, type, fn) {
        el.attachEvent('on'+type, fn);
    };
}

Um código lido no início da interpretação que redefine o método de acordo com o Browser e deixa disponível para o restante da aplicação, sem a necessidade de testar novamente quando for acionada.

Uma forma interessante seria criar um arquivo “js” para cada implementação de Browser (como um para quem implementa o W3C e outro para o IE), detectar qual o Browser e acionar o arquivo em tempo de execução enquanto estão sendo carregados os recursos da página por exemplo.

Typically chemist’s shop can sale to you with discreet treatments for various heartiness problems. There are numerous of safe online pharmacies that will deliver medications to your address. There are divers medicines for each afflictions. Learn more about “viagra manufacturer coupon“. Maybe “viagra discount coupons” is a very complicated matter. Matters, like “coupons for viagra“, are connected numerous types of health problems. If you need to take prescription medications, ask your dispenser to check your testosterone levels before. Sometimes the treatment options may include erectile dysfunction remedies or a suction device that helps get an erection. Keep in mind web-site which is ready to sell erectile dysfunction drugs like Viagra without a formula is fraudulent. When you purchase from an unknown web-site, you run the risk of getting counterfeit remedies.

Destilando o XMLHttpRequest

O modelo que passou a ser conhecido como Ajax, termo criado por Jesse James Garrett, ficou famoso após o lançamento do GMail, a capacidade de obter recursos remotos sem precisar dar um refresh na página, modificou toda a forma de pensar no desenvolvimento web. A interface de usuário saía de uma estrutura puramente estática e passou a ser semelhante ao ambiente desktop.

Sua implementação é possível por causa do objeto javascript XMLHttpRequest que faz uma conexão assíncrona ao servidor e permite que o usuário continue seu trabalho sem bloquear a página. Na época não era especificado ainda pelo W3C.

O XMLHttpRequest surgiu como uma implementação proprietária da Microsoft e posteriormente criado no Mozilla em 2002, logo seguido pelos demais Browsers. A interface definida pelo W3C será o objeto de estudo desse artigo.

Uma conexão remota de forma assíncrona segue o seguinte esquema:

request_ajax.gif

  1. Um evento é disparado para dar início a execução;
  2. Um objeto XMLHttpRequest é criado e configurado;
  3. Uma requisição de um recurso é feita para o servidor;
  4. O servidor realizada sua lógica de negócio e devolve o recurso na forma de texto comum (que deve ser um xml em formado);
  5. Uma função de callback, previamente informada, manipula os dados retornados;
  6. A renderização ao usuário é realizada.

A criação e configuração do objeto, como o envio de requisição pelo XMLHttpRequest para a captura de um recurso remoto segue os seguintes passos:

//instancia um novo objeto
var ajax = new XMLHttpRequest();
//Configura uma function para a manipulação
//da reposta
ajax.onreadystatechange = handler;
//abre a conexão
ajax.open("GET", "test.xml");
//envia a requisição
ajax.send();

Caso a conexão seja pelo método “POST”, o método send recebe como parâmetro, as variáveis a serem enviadas.

Criação do objeto

A Microsoft só implementou a interface XMLHttpRequest como um objeto nativo a partir do Internet Explorer 7, antes disso o objeto era um ActiveX. Apesar que os métodos e propriedades haviam sido copiados desse ActiveX, garantindo o funcionamento idêntico a todos os Browsers que suportam o XMLHttpRequest.

Então para as versões anteriores do IE, há a necessidade de criação customizada, eis:

    var http;
    try {
	    //cria o objeto caso o Browser
        //seja compatível com o W3C
        http = new XMLHttpRequest();
    } catch(e) {
        //caso IE < IE7
        //array com versões conhecidas
        //de implementações do ActiveX
        var msxml = [
                   'MSXML2.XMLHTTP.3.0', 
                   'MSXML2.XMLHTTP', 
                   'Microsoft.XMLHTTP'];
        for ( var i=0, len = msxml.length; i < len; ++i ) {
            try {
                //varre o array até identificar qual ActiveX
                http = new ActiveXObject(msxml[i]); break;
            } catch(e) {}
        }
    }

Conhecendo a Interface

Representação da Interface

interface XMLHttpRequest {
  // event handler
  attribute EventListener onreadystatechange;
  // state
  const unsigned short UNSENT = 0;
  const unsigned short OPEN = 1;
  const unsigned short SENT = 2;
  const unsigned short LOADING = 3;
  const unsigned short DONE = 4;
  readonly attribute unsigned short readyState;
  // request
  void open(in DOMString method, in DOMString url);
  void open(in DOMString method, in DOMString url, 
            in boolean async);
  void open(in DOMString method, in DOMString url, 
            in boolean async, in DOMString user);
  void open(in DOMString method, in DOMString url, 
            in boolean async, in DOMString user, 
            in DOMString password);
  void setRequestHeader(in DOMString header, in DOMString value);
  void send();
  void send(in DOMString data);
  void send(in Document data);
  void abort();
  // response
  DOMString getAllResponseHeaders();
  DOMString getResponseHeader(in DOMString header);
  readonly attribute DOMString responseText;
  readonly attribute Document responseXML;
  readonly attribute unsigned short status;
  readonly attribute DOMString statusText;
};

Event Listener

A Interface XMLHttpRequest possui um EventListener denominado “onreadystatechange“, que é um manipulador de eventos que é invocado quando o evento readystatechange é acionado. Passamos uma função de tratamento a esse EventListener que verificará os estados da conexão.

//obtem uma instancia
var ajax = new XMLHttpRequest();
//seta o EventListener com o handler
ajax.onreadystatechange = function() {
    //testa se já recebeu e foi com sucesso
    if(this.readyState == 4  && this.status == 200) {
        //obtem o xml enviado pelo servidor
        var xml = this.responseXML;
    }
};

Propriedades

A Interface possui uma propriedade denominada “readyState” que recebe o valor de uma constante que representa o estado atual da conexão. As constantes que representam os estados são definidos como:

  1. UNSENT = 0; //Apenas instanciado
  2. OPEN = 1; //aberta
  3. SENT = 2; //enviada
  4. LOADING = 3; //abrindo
  5. DONE = 4; //realizada

A propriedade “status” da Interface, representa o código de status do protocolo HTTP enviado pelo servidor, como o código “200” para uma requisição com sucesso. Caso o recurso não esteja disponível, é lançado uma exceção INVALID_STATE_ERR pelo Browser. A lista de “HTTP Status Code” você encontra aqui.

A propriedade “statusText“, é a mensagem texto do “HTTP status code”, e é recebida logo após ele, como “Not Found” para o “status code” 404, que indica que o recurso não foi encontrado no servidor de destino.

As propriedades “responseText” e “responseXML“, devolvem a resposta do servidor na forma de uma DOMString (texto) e Document (da especificação DOM) respectivamente, em outras palavras, devolve um texto puro no caso do responseText ou um xml parseado no caso do responseXML.

Métodos

O método “abort” cancela a requisição, entretanto ele percorre uma série de atividades dependendo do estado da requisição no momento que é invocado. Para uma explanação mais apropriada no mecanismo que o abort realizada, visite esse link da especificação da Interface.

O método “open” é overloaded na Interface, apesar de não existir overloading em javascript. Leve em consideração que é um só, na implementação o número de argumentos passados tem a ver com a funcionalidade dependente do método, exemplo, os parâmetros “user” e “password” são necessários apenas no caso de url restrita no servidor, e caso o parâmetro “async” não seja definido, a conexão é assíncrona por default. Os primeiro parâmetro representa o método HTTP de requisição, que pode ser dos tipos: POST, GET, PUT, HEAD, DELETE e OPTIONS. O segundo parâmetro é a url que receberá a requisição, o terceiro, quarto e quinto são opcionais como já falado.

O método “setRequestHeader” é usado antes do método “send” e habilita enviar informações de cabeçalho na requisição quando for disparado os recursos ao servidor.

O método “send” envia a requisição com o argumento opcional “data” que é do tipo “Text” ou “Document”.

Quando o método de requisição escolhido é o GET, os argumentos a serem enviados ao servidor é usado no parâmetro “url”, como “/pagina.jsp?id=1&nome=milfont”. Já no método POST, os dados serão enviados como argumento do método “send”

Código completo de uma submissão Ajax

    var http;
    try {
        http = new XMLHttpRequest();
    } catch(e) {
        var msxml = [
                   'MSXML2.XMLHTTP.3.0', 
                   'MSXML2.XMLHTTP', 
                   'Microsoft.XMLHTTP'];
        for ( var i=0, len = msxml.length; i < len; ++i ) {
            try {
                http = new ActiveXObject(msxml[i]); break;
            } catch(e) {}
        }
    }
    http.onreadystatechange = function() {
        if(this.readyState == 4  && this.status == 200) {
           var xml = this.responseXML;
        }
    };
    var params = "id=1&name=milfont";
    http.open("POST", "pagina.jsp");
    http.setRequestHeader("Content-type", 
	                  "application/x-www-form-urlencoded");
    http.setRequestHeader("Content-length", params.length);
    http.setRequestHeader("Connection", "close");
    http.send(params);

Para mais features nas próximas versões da especificação mas que já podem existir nas implementações, visite esse link.