O Google anunciou o lançamento de um portal chamado “Chrome Experiments” que compila uma série de games e visualizações experimentais desenvolvidos em JavaScript por profissionais em várias partes do mundo.
Vale apena ver, tem muitas coisas legais.
Site: http://www.chromeexperiments.com
‘CSS’ Category
-
Novo lançamento da Google, confira o portal Google Chrome Experiments
March 19, 2009 by admin
Category JavaScript | Tags: browser,chrome,google,inteface,JavaScript | No Comments
-
Memory Leak
March 17, 2009 by admin
Contents
Identificação do problema
Quando desenvolvemos aplicativos com DHTML (Javascript, DOM e CSS) encontramos problemas para controlar a performance e os memory leaks. Uma vez identificados os padrões de códigos que causam esses problemas, começaremos a trabalhar de forma homogênea e sem a necessidade de retrabalho.
O problema não atinge só o Internet Explorer 6.0 (IE6). Ele pode acontecer com o Mozilla, Netscape e Opera. Apesar do IE6 ser o browser mais problemático hoje em dia. Muitos aplicativos, com determinado tempo de uso, começam a consumir uma quantidade impressionante de memória causando lentidão na máquina do usuário e refletindo na experiência que ele tem ao acessar o site. Muitas vezes a memória só é devolvida ao sistema operacional depois de fechar o browser.
Então o que acontece para existir o memory leak? Como é muito comum supor, o problema não está no garbage colector (GC) dos browsers. Pelo contrário. Eles funcionam muito bem se você tomar determinados cuidados na hora de programar. Considere o seguinte código:
function GCTest() { var x = new Array(1000).join(new Array(2000).join('XXXXX')); }Após esta função ser executada, toda a memória utilizada pela variável x é devolvida para o sistema operacional pelo GC do Javascript (COM). O mesmo acontece quando trabalhamos com elementos do DOM. Quando criamos uma tabela com HTML, não precisamos nos preocupar em remover essa tabela da memória após a execução da página.
<table> <tr><td>Lorem</td><td>Ipsum</td></tr> </table>
Quando trabalhamos com os “dois mundos”, o COM guarda uma referência para cada elemento. Se existe referência para um elemento no COM, a memória usada pela por ele não é liberada. O problema acontece quando temos uma referência cruzada, onde um objeto Javascript aponta para um objeto DOM e o DOM aponta de volta para o Javascript, e assim a memória usada nunca será liberada.
Padrões de memory leak
Inline script
Na declaração inline de um script na criação de um elemento HTML a referência que é criada para a função foo() não é eliminada depois que o código é executado. Isso cria uma referência circular e causa o memory leak.
function LeakMemory() { for (var i = 0; i < 5000; i++) {var parentDiv = document.createElement(‘
‘);} }
Nesse código criamos um novo elemento div sem a declaração do evento. O evento deve ser adicionado depois que o elemento já existe no DOM para evitar o memory leak. Note que nesse exemplo o número de iterações passa de 5.000 para 50.000, e mesmo assim a memória é liberada após a execução do escopo da função.
function LeakMemory() { for (var i = 0; i < 50000; i++) { var parentDiv = document.createElement('div'); } }Closures
Closures é um pattern muito utilizado em Javascript que permite o encapsulamento de funções. Leia mais sobre o assunto em Referências. Neste caso, adicionamos o evento onclick após a criação do elemento HTML.
function LeakMemory() { var parentDiv = document.createElement('div'); parentDiv.onclick = function() { foo(); }; }Quando criamos o elemento parentDiv e adicionamos o evento onclick dentro da função principal, criamos um closure. Ele guarda uma referência no elemento DOM (parentDiv) e o Javascript (função anônima).
Expando property
No código a seguir, criamos uma referência circular guardando a referência da variável global do Javascript que aponta para um elemento DOM dentro de uma propriedade do elemento DOM. O objeto Javascript myGlobalObject aponta para o objeto DOM (div) LeakDiv que tem a propriedade expandoProperty que aponta de volta para o elemento Javascript myGlobalObject.
var myGlobalObject; function SetupLeak() { myGlobalObject = document.getElementById('LeakedDiv'); document.getElementById('LeakedDiv').expandoProperty = myGlobalObject; }Encapsulator pattern
Esse caso é muito semelhante ao anterior só que a referência circular é feita guardando a referência do DOM dentro da função Javascript e a referência Javascript dentro da propriedade do objeto DOM. O objeto Javascript this.elementReference aponta para o objeto DOM (div) element que tem a propriedade expandoProperty que aponta de volta para o elemento Javascript.
function Encapsulator(element) { this.elementReference = element; element.expandoProperty = this; } function SetupLeak() { new Encapsulator(document.getElementById('LeakedDiv')); }Event listener
Esse é o caso mais comum de memory leak com closures, onde o evento onclick faz a referência circular por usar uma função anônima dentro do mesmo escopo da criação do listener.
window.onload = function() { var obj = document.getElementById('LeakedDiv'); obj.onclick = function(e) { ... lógica ... }; }Cross Page Leak
Aqui temos um caso que é muito difícil de identificar. Nesse caso, a ordem de inserção dos novos elementos na árvore do DOM influencia. Na criação do parentDiv e do childDiv foi usado inline script e quando adicionamos os elementos na página ele perde a referência porque os elementos apontam para funções anônimas.
function LeakMemory() { var hostElement = document.getElementById('hostElement'); for (i = 0; i < 5000; i++) {var parentDiv = document.createElement(‘
‘);
var childDiv = document.createElement(‘‘);parentDiv.appendChild(childDiv); hostElement.appendChild(parentDiv); hostElement.removeChild(parentDiv); parentDiv.removeChild(childDiv); parentDiv = null; childDiv = null; } hostElement = null; }
Novamente, a melhor opção é trabalharmos com funções fora do escopo da criação dos elementos e, se necessário, ao inserir no DOM sempre fazermos a inserção do elemento pai e depois o elemento filho como é mostrado no exemplo seguinte.
function LeakMemory() { var hostElement = document.getElementById('hostElement'); for (i = 0; i < 5000; i++) {var parentDiv = document.createElement(‘
‘);
var childDiv = document.createElement(‘‘);hostElement.appendChild(parentDiv); parentDiv.appendChild(childDiv); parentDiv.removeChild(childDiv); hostElement.removeChild(parentDiv); parentDiv = null; childDiv = null; } hostElement = null; }
Boas práticas
Referências ao DOM
É recomendável que ao criar uma variável Javascript que aponte para um elemento DOM ela seja desalocada logo que deixar de ser usada. Para isso atribuímos null à variável. Essa regra se aplica tanto para variáveis privadas como para as privilegiadas.
function domRefecente() { var header = document.getElementById('headerDiv'); var footer = document.getElementById('footerDiv'); this.div = document.createElement('div'); ... lógica ... header = null, footer = null, this.div = null; }Quando é necessário guardar um objeto DOM no Javascript para utilização futura, SEMPRE guarde o id do objeto ao invés de uma referência para ele.
function domRefecente() { var div = document.createElement('div'); div.id = 'meuDiv'; var divId = div.id; div = null; }Eventos
Não usar funções anônimas para os eventos. Exemplo da correta declaração de um evento.
function addListener() { var elm = document.getElementById('myDiv'); elm.onclick = handleClick; elm = null; }function handleClick(e) { ... lógica ... }Ao atribuir um novo evento a um elemento, ele já deve existir na árvore do DOM. Exemplo da correta atribuição de evento a um novo elemento que está sendo criado via Javascript.
function newElement() { var elm = document.createElement('div'); document.appendChild(elm); elm.onclick = handleClick; elm = null; }function handleClick() { ... lógica ... }Quando um elemento é removido do DOM via Javascript, todos os eventos cadastrados nele devem ser removidos para evitar o memory leak.
function createElements() { var ul = document.getElementById('menu'); for (var i = 0; i < 5000; i++) { var li = document.createElement('li'); li.id = 'menu' + i; ul.appendChild(li); li = null; } addListener(); ul = null; }function addListener() { for (var i = 0; i < 5000; i++) { var li = document.getElementById('menu' + i); li.onclick = handleClick; li = null; } }function handleClick(e) {
... lógica ...
}
function removeElements() { for (var i = 0; i < 5000; i++) { var li = document.getElementById('menu' + i); li.onclick = null; li.parentChild.removeChild(li); li = null; } }Criando elementos no DOM
A ordem com que os elementos são inseridos no DOM via Javascript influencia na performance e na criação de memory leaks. O ideal é termos uma outra função fora do escopo da função usada para a criação dos elementos onde serão atribuídos os eventos a esses elementos.
Quando estamos inserindo uma série de elementos no DOM, a cada appendChild o browser renderiza esse elemento juntamento com todos os filhos do elemento (elemento pai) aonde estamos inserindo ele. O que isso significa? Quando temos um div com vários div’s dentro dele, ao inserir um novo div, todos os anteriores serão renderizados novamente, consumindo uma quantidade enorme de tempo proporcional a quantidade de elementos no div pai.
Nesse caso usamos a técnica de criar um elemento temporário, adicionar todos os div’s filhos dentro dele e só então adicioná-los ao div pai.
function addDiv() { var divPai = document.getElementById('divPai'); var divTmp; for (var i = 0; i < 5000; i++) { var divFilho = document.createElement('div'); divTmp.appendChild(divFilho); divFilho = null; } divPai.appendChild(divTmp); divPai = null, divTmp = null; }Existe o caso em que precisamos adicionar o evento nos elementos na hora da sua criação. Para tornar isso possível, podemos inverter a ordem da inclusão dos elementos como mostrado no item Cross Page Leak.
Conclusão
Com um pouco de atenção e conhecimento podemos evitar muita dor de cabeça no futuro. Facilitando a manutenção do código e o acesso dos usuários aos nossos sites. O memory leak é apenas um dos problemas que temos de nos preocupar na hora de criar um aplicativo web. Por isso que um pouco de planejamento antes de começar a codificar evita muitas horas de depuração de código e retrabalho.
Programas
Veja no item específico da lista de Softwares para Interface #Memory Leak.
Referências
- Javascript Closures
- Memory Leakage in Internet Explorer – revisited
- Memory leak patterns in JavaScript
- IE: where’s my memory?
- DHTML Leaks Like a Sieve
- Javascript memory leaks
Category JavaScript | Tags: DOM,inteface,JavaScript,memory leak | 1 Comment
-
SmartSprites – Css sprites gerados automaticamente
March 16, 2009 by admin
Este tutorial está bem detalhado, faça um teste seguindo passo-a-passo, e logo estará apto à aplicar em qualquer site!
1) Faça o download (http://smartsprites.osinski.name/download.html) da ferramenta SmartSprites, descompacte em um diretório qualquer (aqui estou usando o meu diretório padrão de projetos, que é c:\gonow\developer).
2) Vamos fazer a marcação do css, necessária para a ferramenta se identificar. Fazemos isso usando comentários.
O sprite é composto por várias imagens, unidas em uma só e exibidas pelo posicionamento do bg de cada elemento;
Para criar um sprite automaticamente devemos criar instruções para o SmartSprites entender que imagem pertence ao sprite x;
Criamos referência ao primeiro Sprite, usando o seguinte código dentro do css:
/** sprite: mysprite; sprite-image: url('../img/mysprite.png'); sprite-layout: vertical */ou ainda:
/**sprite: mysprite; sprite-image: url('../img/mysprite.png'); sprite-layout: vertical */-Explicando esse código temos:
sprite // nome do sprite, será usado futuramente para referenciar cada imagem que compõe o sprite; sprite-image // nome e caminho da imagem que será gerada pelo programa; sprite-layout // orientação da imagem (vertical | horizontal);
Obs: deve vir antes de qualquer referência a este sprite, mas não precisa estar no topo do css.
Existem ainda outras opções, recomendo o acesso ao site, e pesquisas na internet para conhece-las mais a fundo, já existe bastante coisa a respeito na internet, e no fim deste post vou mencionar alguns links.
Após criar a regra do primeiro sprite, já podemos começar a definir que fará parte dele, para isso vamos atribuir na frente da propriedade background-image um outro comentário, como este:
background-image:url(../img/xpto.png); /** sprite-ref: mysprite; repeat; sprite-margin-top: 5px */
Explicando esse código temos:
background-image:url(../img/xpto.png); // propriedade obrigatória, indica qual imagem será incluida no sprite;
e nos comentários:
sprite-ref: mysprite; //Nome usado anteriormente na instancia, diz que essa imagem deve estar no sprite mysprite; repeat; //Avisa que permite repetição, então deve-se estender a imagem na direção da repetição (x = horizontal | y = vertical) sprite-margin-top: 5px // distância em relação a outras imagens;
Esse comentário deve estar presente em todas as regras de css que contenham imagens de bg para o sprite, mas, em nenhum dos casos, a propriedade background poderá ser usada de forma abreviada para funcionar, em um exemplo prático, o código abaixo:
background: transparent url("../img/center_bg.png") no-repeat center top;
irá virar:
background-color: transparent; background-image: url("../img/center_bg.png") ; background-repeat:no-repeat; background-position:center top;
sendo que o background-repeat se tornou descartável e o background-position será gerado pela ferramenta, então podemos reduzir ainda para:
background-color: transparent; background-image: url("../img/center_bg.png") ;
e aplicando as intruções para o smartSprite:
background-color: transparent; background-image: url("../img/center_bg.png") ;/** sprite-ref: mysprite; */
Finalizada esta explicação, segue um exemplo de código real, antes da formatação:
#logo{ background:url("../img/logo.png") no-repeat top left; width:180px; height:57px; left: 28px; position: absolute; top: 40px; } #center { background: transparent url("../img/center_bg.png") no-repeat center top; padding-top: 10px; position: relative; } #left #leftContent { position: relative; overflow: auto; background: transparent url("../img/homeContentBg.png") repeat-x center bottom; width: 284px; }
E agora, após configurado para o smartSprites:
#logo{ background-image:url("../img/logo.png");/** sprite-ref: mysprite; */ width:180px; height:57px; left: 28px; position: absolute; top: 40px; } #center { background-image: transparent url("../img/center_bg.png");/** sprite-ref: mysprite; */ padding-top: 10px; position: relative; } #left #leftContent { position: relative; overflow: auto; background-image: url("../img/homeContentBg.png");/** sprite-ref: mysprite; */ width: 284px; }
Usei a forma mais simples para mostrar como pode se relacionar uma imagem atual à uma imagem de sprite, você pode aplicar diversas outras configurações, como alinhamento, margem, etc;
3) Após concluir a aplicação dos comentários no css temos que fazer com que a ferramenta processe o que foi pedido, para isso, acessamos via prompt de comando o diretório onde descompactamos o smartSprites (C:\gonow\developer\smartsprites) e digitamos a seguinte linha:
smartsprites --root-dir-path c:/example
Onde c:/example é o diretório raiz do projeto, onde o css esteja visível;
4) Se chegou até aqui, o prompt terá exibido uma tela com várias [INFOS], então você ja gerou seus sprites.
Vá até a pasta definida para os sprites no primeiro comentário, veja sua nova imagem, vá até a pasta de css e veja seu novo css ja configurado com as positions do sprite, e altere o html do seu site para chamar o novo css. Faça um preview e tire suas conclusões.Observações
- O programa já vem com um diretório contendo vários exemplos, é interessante executá-los para ver o resultado final. - Acesse o faq: http://smartsprites.osinski.name/#faq
Links Externos
- http://smartsprites.osinski.name/ - http://framebox.blogspot.com/2009/01/implementando-css-sprites-com.html
Category CSS | Tags: CSS,css sprites,padroes web,tableless | No Comments