Já foi publicado aqui no desenvolvimento para web um artigo com equivalentes nativos de JavaScript para funções jQuery, com dicas sobre manipulação de DOM, classes CSS e modificação de propriedades com JavaScript nativo.
Tal como um complemento àquele, este artigo também aborda algumas soluções com “vanilla JS” (como também é chamado o JavaScript nativo ou puro, sem o uso de bibliotecas) que, certamente, são de grande ajuda e fazem muitos projetos dispensarem o uso de jQuery em detrimento a uma solução nativa mais rápida e igualmente eficiente.
jQuery tem sido uma dádiva para, praticamente, todos nós desenvolvedores front-end desde seu lançamento, com seus métodos intuitivos e funções fáceis. JavaScript é difícil, é difícil de “entrar” e é muito mais difícil do que jQuery. Mas o tempo não pára e “ser nativo” pode ser o futuro de front-end: HTML5.
HTML5 não significa apenas alguns elementos HTML extras; HTML5 abrange uma grande “massa” de tecnologia e, também, junto com ele vem ECMAScript 5, o futuro do JavaScript. Combinando APIs HTML5 que necessitam de JavaScript, em sua maioria, precisamos adotar uma estrutura mais nativa de trabalhar a cada dia em que jQuery e afins se tornam menos importantes.
Este artigo apresenta o amante de jQuery a alguns dos mais duros e mais incompreendidos métodos JavaScript, funções e muito mais para mostrar como a tecnologia nativa não é tão difícil quanto parece e que o JavaScript nativo provavelmente vai bater em você como um tijolo no rosto muito em breve – se isso já não aconteceu. Este artigo serve para mostrar a qualquer um que é possível mergulhar em desenvolvimento nativo de JavaScript em detrimento a jQuery e, quem sabe, abrir algumas portas para o futuro da (sua) codificação.
Seletores
Seletores são o grande vendedor de jQuery. Não é preciso, sequer, pensar nisso: a seleção de elementos com jQuery é feita sem pensar, é super simples. jQuery usa Sizzle, um motor também criado pela Fundação jQuery (mas disponível standalone) para usar como motor de seleção. O poderoso código por trás do Sizzle vai fazer você pensar duas vezes antes de complicar seus seletores e a alternativa JavaScript crua vai fazer você pensar duas vezes sobre jQuery completamente!
Seletores de classe
JavaScript não tinha método nativo className
para pegar elementos com classes até recentemente, o que dificultou sua popularidade desde o início. Classes são as melhores para o desenvolvimento HTML/CSS, mas não foram bem suportadas com JavaScript nativo. Até agora.
Dê uma olhada nas opções:
1 2 3 4 5 |
// jQuery $('.myClass'); // JavaScript document.getElementsByClassName('myClass'); |
Isso retorna um NodeList. Um Nó é um termo JavaScript para o elemento Object
e um NodeList
é uma lista ordenada de Nós.
NodeList
e, então, você tem que lidar com ele. jQuery cuida de tudo isso para você, encobrindo o que realmente está acontecendo; mas é muito importante saber o que está acontecendo.
Seletores de ID
O mais fácil do pacote:
1 2 3 4 5 |
// jQuery $('#myID'); // JavaScript document.getElementById('myID'); |
Retorna um Nó simples.
Tags
Tão fácil quanto o seletor de ID, o seletor de tag também retorna um NodeList
:
1 2 3 4 5 |
// jQuery $('div'); // JavaScript document.getElementsByTagName('div'); |
querySelector
/querySelectorAll
Quando entramos em querySelector, as coisas começam a esquentar. Se não fosse pela jQuery, querySelector
poderia hoje não existir em JavaScript e ser tão rápido e/ou tão eficiente quanto o é e, por isso, temos muito o que agradecer à jQuery!
A mágica por trás de querySelector é impressionante, é uma ferramenta nativa multiuso que pode ser usada em vários casos – e isso é JavaScript puro. Existem dois tipos de querySelector: o primeiro que é document.querySelector()
retorna o primeiro Nó do NodeList
, independentemente de quantos Objetos-Nó possam ser encontrados; o segundo é document.querySelectorAll()
, que retorna um NodeList
toda vez.
Veja alguns exemplos (leia os comentários para entender melhor):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/* * Classes */ // Pega o primeiro nome de classe .myClass document.querySelector('.myClass'); // Retorna um NodeList com todas instâncias de .myClass document.querySelectorAll('.myClass'); /* * ID */ // Pega o ID myID document.querySelector('#myID'); /* * Tags */ // Retorna um NodeList com todas instâncias de 'div' document.querySelectorAll('div'); |
querySelectorAll
é poderoso, e, definitivamente, o futuro. Ele também suporta seletores mais complicados:
1 2 3 4 5 |
// Pega o último Nó de lista de .someList document.querySelector('ul.someList li:last-child'); // Pega algum atributo data-* document.querySelectorAll('[data-toggle]'); |
Também é possível criar uma função wrapper inteligente para isso, para evitar de ficar digitando document.querySelectorAll()
toda vez:
1 2 3 4 5 |
var _ = function( elem ) { return document.querySelectorAll( elem ); } // Uso var myClass = _('.myClass'); |
Também é possível usar o símbolo $
em vez de um sublinhado, isso é com você. Não é o ideal começar uma expressão de função com um sublinhado, mas isso foi para mostrar que também é possível e aceito.
IE8 suporta seletores CSS2 para querySelector
, mas, idealmente, você deve usar nomes de classes eficientes e seletores mínimos.
Manipulação de classe
É possível estender JavaScript usando um método de herança por prototipagem, que é o que jQuery faz nos bastidores. HTML5, porém, é o futuro, e está crescendo enquanto navegadores antigos estão rapidamente diminuindo. É hora de começar a usar métodos nativos de JavaScript para classes, como o novo recurso de HTML5 classList
. Vamos a algumas comparações com jQuery.
Adicionar classe
Adicionar uma classe é fácil com jQuery, ela faz tudo para você, cuidando do array de NodeList
também.
1 2 3 4 5 6 |
// jQuery $('div').addClass('myClass'); // JavaScript var div = document.querySelector('div'); div.classList.add('myClass'); |
Remover classe
Assim como o exemplo anterior, super simples:
1 2 3 4 5 6 |
// jQuery $('div').removeClass('myClass'); // JavaScript var div = document.querySelector('div'); div.classList.remove('myClass'); |
Alternar classe
Alternar é algo realmente importante para a linguagem e, muitas vezes, difícil de fazer através de métodos prototype. Felizmente, aqui está:
1 2 3 4 5 6 |
// jQuery $('div').toggleClass('myClass'); // JavaScript var div = document.querySelector('div'); div.classList.toggle('myClass'); |
Arrays
Agora vamos entrar num dos aspectos mais avançados de JavaScript: Arrays. Arrays são usados para armazenar valores dentro de uma variável, tal como:
1 |
var myArray = ['one', 'two', 'three', 'four']; |
jQuery torna isso muito fácil com o método $.each()
; método este que esconde um pouco do trabalho sujo e torna as coisas fáceis. JavaScript começou com nenhuma funcionalidade embutida para iterar em arrays, por isso estamos acostumados a trabalhar manualmente os itens no array usando a propriedade length
e iterar sobre cada item, de forma incremental, dentro de um loop for
:
1 2 3 4 |
var myArray = ['one', 'two', 'three', 'four'] for ( var i = 0; i < myArray.length; i++ ) { // ... } |
Recentemente aconteceu um upgrade e surgiu o método forEach
, que é mais lento que o mostrado acima, mas provê funcionalidade de callback similar à $.each()
de jQuery:
1 2 3 4 5 6 7 8 9 10 |
// Usando o array no início... ['one', 'two', 'three', 'four'].forEach(function(){ // ... }); // Ou algo mais "oldschool" com a declaração da variável var myArray = ['one', 'two', 'three', 'four']; myArray.forEach(function(){ // ... }); |
Olhando para o lado do jQuery das coisas, aqui está uma rápida comparação dos dois:
1 2 3 4 5 6 7 8 9 10 11 12 |
// jQuery var myArray = ['one', 'two', 'three', 'four'] $.each( myArray, function ( index, value ) { console.log(value); }); // JavaScript var myArray = ['one', 'two', 'three', 'four'] for ( var i = 0; i < myArray.length; i++ ) { var value = myArray[i]; console.log( value ); } |
Looping em NodeList
Uma grande diferença em relação à jQuery é o fato de que, em JavaScript puro, precisamos gerar um loop usando getElementsByClassName()
ou querySelectorAll()
. Por exemplo, em jQuery, quer seja em uma classe ou num NodeList
, o código é idêntico! O mesmo não acontece com JavaScript nativo. Por exemplo, para adicionar uma classe em ambos (note a diferença nos últimos dois métodos JavaScript):
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// jQuery var someElem = $('.someElem'); someElem.addClass('myClass'); // JavaScript: adiciona a classe à somente o primeiro Nó var someElem = document.querySelector('.someElem'); someElem.classList.add('myClass'); // JavaScript: adiciona a classe a todos os Nós do NodeList var someElem = document.querySelectorAll('.someElem'); for ( var i = 0; i < someElem.length; i++ ) { someElem[i].classList.add('myClass'); } |
Então, qual a diferença aqui? Recebemos um NodeList
retornado e, portanto, é preciso iterar neste para aplicar uma nova classe a cada Nó. Muito simples e faz sentido. Este é o tipo de coisas avançadas jQuery cuida para nós. A coisa com JavaScript é que ele é muito assustador no começo, mas, uma vez que você começou, é viciante e é imperativo saber o que está acontecendo “por debaixo dos panos”, como diz o ditado.
Attributos, setting, getting e remoção
JavaScript oferece métodos melhores e mais descritivos (mas um pouco mais longos na contagem de caracteres) para lidar com atributos. Veja as diferenças.
Atributos set
Em jQuery, a convenção de nomenclatura não é tão boa como em JS nativo, como o attr()
, que pode fazer o callback de um valor, bem como o definir, de forma que é inteligente, mas, para quem quer aprender isso, pode causar alguma confusão. Vejamos como podemos definir atributos em ambos:
1 2 3 4 5 |
// jQuery $('.myClass').attr('disabled', true); // JavaScript document.querySelector('.myClass').setAttribute('disabled', true); |
Remover atributos
Remover atributos também é fácil:
1 2 3 4 5 |
// jQuery $('.myClass').removeAttr('disabled'); // JavaScript document.querySelector('.myClass').removeAttribute('disabled'); |
Atributos get
É assim que se mostra o valor de atributo no Console:
1 2 3 4 5 |
// jQuery console.log($('.myClass').attr('title')); // JavaScript console.log(document.querySelector('.myClass').getAttribute('title')); |
Atributos data-*
Atributos HTML5 data-*
são, provavelmente, uma das melhores adições para a especificação HTML. jQuery possui .data()
. Veja a comparação:
1 2 3 4 5 6 7 8 9 |
<div class="myElem" data-username="Todd"></div> <script> // jQuery console.log($('.myElem').data('username')); // Logs 'Todd' // JavaScript: usa o método getAttribute console.log(document.querySelector('.myElem').getAttribute('data-username')); </script> |
HTML5 introduz a API do conjunto de dados (dataset), que não tem suporte tão ruim; mas, para o uso mais intenso de data(), é recomendado, por enquanto, o uso de jQuery, já que funciona em todos os navegadores, mesmo nos legados.
Um outro excelente artigo a respeito de dataset API e o que é possível fazer usando vanilla JS é o “The element.dataset API“, que, realmente, vale a pena a leitura!
Análise (parsing) de JSON
Há truques possíveis de serem feitos para o parsing de JSON e criação de objetos com o puro e simples JavaScript. É praticamente a mesma coisa! Considere o exemplo de pegar um atributo customizado data-*
para um JSON e o “analisar” (fazer o parsing) para, em seguida, fazer um hook para este mesmo objeto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<div class="myElem" data-user='{ "name" : "Todd", "id" : "01282183" }'></div> <script> // jQuery var myElem = $('.myElem').data('user'); // Pega o JSON var myJSON = $.parseJSON(myElem); // Parsing da string no objeto JSON console.log(myJSON.name); // Objeto JSON, retorna 'Todd' console.log(myJSON.id); // Objeto JSON, retorna '01282183' // JavaScript var myElem = document.querySelector('.myElem').getAttribute('data-user'); var myJSON = JSON.parse(myElem); console.log(myJSON.name); // Objeto JSON, retorna 'Todd' console.log(myJSON.id); // Objeto JSON, retorna '01282183' </script> |
Eventos
Eventos desempenham um papel enorme em JavaScript e tem tido uma má reputação no passado devido a problemas de compatibilidade entre navegadores. Um evento simples de clique em jQuery se parece com:
1 2 3 |
$(elem).click(function (){ // ... }); |
Ou, para usar o que existe de mais atual e é recomendado:
1 2 3 |
$(elem).on('click', function (){ // ... }); |
Sendo possível, também, encadear os eventos:
1 2 3 |
$(elem).on('click focus keyup', function (){ // ... }); |
Isso vincula alguns eventos à função. Qualquer um deles vai executá-la. Sem mencionar que você pode facilmente retirar ou acrescentar algum outro.
Há, também, a delegação de eventos com elementos JavaScript criados dinamicamente:
1 2 3 |
$(parent).on('click', elem, function (){ // ... }); |
Isso captura o evento DOM através de um evento-pai ouvinte (parent event listener), tal como explicado no artigo sobre como otimizar códigos jQuery e aumentar a performance do front-end.
Voltando à comparação entre jQuery e JS nativo, aqui estão alguns exemplos de manipuladores de eventos (event handlers):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/* * Click */ // jQuery $(elem).on('click', function() {...}); // JavaScript document.querySelector(elem).onclick = function() {...} /* * Submit */ // jQuery $(elem).on('submit', function() {...}); // JavaScript document.querySelector(elem).onsubmit = function() {...} /* * Change */ // jQuery $(elem).on('change', function() {...}); // JavaScript document.querySelector(elem).onchange = function() {...} |
Mas há um problema com os manipuladores de eventos de JavaScript, e você pode culpar a Micro$oft por seu attachEvent
: foi decidido implementar sua própria rota não-padrão e usar attachEvent quando todos os outros navegadores estavam usando addEventListener
…
Felizmente, existe um script com uma solução simples, fornecida pelo próprio John Resig, que resolve esse problema para nós.
Manipulação de CSS
CSS é, reconhecidamente, mais agradável nos métodos de objeto jQuery; de qualquer maneira, veja a implementação nativa disso com JavaScript, que é muito similar e vale a pena conhecer:
1 2 3 4 5 6 7 8 9 10 11 |
// jQuery $(elem).css({ 'background' : '#F60', 'color' : '#FFF' }); // JavaScript var elem = document.querySelector(elem); elem.style.background = '#F60'; elem.style.color = '#FFF'; |
Os “ganchos” (hooks) acima em com JS nativo permitem definir vários estilos com facilidade.
Função “Document Ready”
jQuery já vem com um manipulador “DOM ready”, em que é possível executar com segurança todas as funções sabendo que a árvore DOM está totalmente preenchida e qualquer manipulação feita irá funcionar e não retornar undefined
.
À medida que avançamos em direção a um futuro de tecnologia surpreendente, os navegadores agora disparam seu próprio manipulador “DOM ready”; em navegadores modernos, é o evento DOMContentLoaded
(já mostrado em “Como loading de JavaScript funciona: DOMContentLoaded e OnLoad“), que pode ser disparado assim:
1 2 3 |
document.addEventListener('DOMContentLoaded', function() { // DOM pronto! }, false); |
Conclusão
Tem havido uma tendência de chamar jQuery de “a solução” e a pensar de que não há outra saída…. Não é tão interessante para os próximos desenvolvedores confiarem somente nela e é imperativo aprender (ou, pelo menos, ter algum conhecimento) JavaScript nativo. Quanto mais poderoso se torna o HTML5, mais poderemos utilizar esses recursos nativos. E quanto mais poderoso os recursos se tornam, menos precisamos de jQuery e mais ela se torna inútil.
Adote as novas tecnologias! Não está sendo sugerido que você jogue fora seu fluxo de trabalho com jQuery e comece a trabalhar com JavaScript nativo instantaneamente, mas um futuro nativo está chegando…
Você está pronto?