Categorias
HTML5 JavaScript Tutoriais

Trabalhando com XMLHttpRequest e Fetch API

Nesse post vamos trabalhar com duas opções para realizar requisições Assíncrona a dados no servidor XMLHttpRequest e Fetch API. Com a popularização do jQuery por muito tempo utilizamos a biblioteca para fazer requisições AJAX pela facilidade, melhorias com recursos extras e o suporte aos browsers antigos.

Mas temos um recurso antigo que nos permite realizar requisições ao servidor com Vanilla JavaScript, o XMLHttpRequest.

XMLHttpRequest

XMLHttpRequest é um objeto XHR para interação com servidores. Com ele podemos requisitar dados sem realizar o reload de todo conteúdo, para você que está programando agora isso pode parecer básico, mas não era possível no começo da web.

Com a popularização do AJAX o XMLHttpRequest foi utilizado como recurso base para o conceito. O nome sugere que a API é focada em XML, mas elas pode resgatar qualquer tipo de dado.

Utilizando XMLHttpRequest

Para esse exemplo iremos fazer uma requisição a um arquivo em nosso projeto:

function onLoadRequest(event) { console.log(this.responseText); } const oRequest = new XMLHttpRequest(); oRequest.addEventListener('load', onLoadRequest); oRequest.open('GET', '/dados.json'); oRequest.send();

No código acima criamos uma objeto XMLHttRequest, onde adicionamos um listener para ouvir quando a requisição for lida. Assim abrimos uma requisição do tipo GET para arquivo dados.json em nosso projeto. E por fim enviamos a requisição com o método “send”

Por padrão essa requisição será assíncrona, isso significa que a execução do código continuará sendo executada e quando os dados forem recebido a função de callback onLoadRequest será executada.

Tratando as resposta

Temos diversos tipos de atributos de respostas a requisição os principais são responseXML e responseText no exemplo anterior tratamos o responseText.

responseXML: é utilizado para tratar requisições feitas para arquivos XML, nesse tipo de requisição podemos trabalhar de quatro formas com: XPath, Parsing e serialização de XML, XMLSerializer e expressões regulares.

responseText: se você deseja ler conteúdo de texto ou de uma página web e injetar em sua página Web, esse é o caminho. Atualmente é o formato mais utilizado na web.

Monitorando o processo

No código anterior, adicionamos um listener para o evento de load mas temos a opção de trabalhar com 5 eventos:

  • progress
  • error
  • abort
  • load
  • loadend(abort, load ou error)
// evento disparado quando a requisição for completa function onLoadRequest(event) { console.log(this.responseText); } // evento disparado quando o download do arquivo tem uma progressão function onProgressRequest (oEvent) { if (oEvent.lengthComputable) { let percentComplete = oEvent.loaded / oEvent.total * 100; console.log('Percentagem:', percentComplete + '%') } else { console.log('O total do arquivo é desconhecido'); } } const oRequest = new XMLHttpRequest(); oRequest.addEventListener('load', onLoadRequest); oRequest.addEventListener('progress', onProgressRequest) oRequest.open('GET', '/dados.json'); oRequest.send();

Caso algo de errado aconteça podemos realizar uma verificação para realizar um tratamento. Assim vamos adicionar uma condicional em nosso código:

function onLoadRequest(event) { if(this.status === 200) { console.log(this.responseText) } else { console.log('Somthing wrong happen:',this.status); } }

No código acima verificamos se o status da requisição é 200(sucesso) caso contrário exibimos o status, dai temos uma várias possibilidades: 404, 401, 500…

Fetch API

Fetch API é uma melhoria sobre a XMLHttpRequest. Ela trás a possibilidade de trabalhar com interface para administrar as requisições de recursos na rede, anteriormente tínhamos XMLHttpRequest para acompanhar requisições na rede, mas era uma API bem limitada comparada com as necessidades das aplicações modernas que temos hoje em dia. Por outro lado sua desvantagem que ela está presente apenas nos browsers modernos.

Principal diferença que agora com Fetch Api podemos ter objetos específicos para requisições(Request) e repostas(Response). Isso permite que elas sejam utilizadas em diferentes situações como por exemplo, Service Workers e Cache API.

Ela também fornece uma definição para conceitos relacionados, como CORS e semântica de cabeçalhos HTTP.

Utilizando fetch API

Para realizar uma requisição com fetch API, chamamos o método fetch() presente no Window ou em um Worker global. O método tem como parâmetro obrigatório o caminho do arquivo ao qual desejamos requisitar. Agora vamos para um exemplo simples com fetch API:

fetch('/dados.json') .then((response) => console.log(response));

Primeira diferença, é a quantidade de linhas que escrevemos, também a opção de se trabalhar com Promises. No caso acima realizamos uma requisição para o mesmo endereço caso sucesso exibimos em nosso console o tipo de resposta que será seguinte, um objeto do tipo Response com as seguintes informações:

body: (...) bodyUsed: false headers: Headers __proto__: Headers ok: true redirected: false status: 200 statusText: "OK" type: "basic" url: "http://127.0.0.1:5500/dados.json"

Com podemos ver temos o corpo de nossa resposta que terá:

  • Nosso conteúdo
  • Status da requisição e do conteúdo
  • Informações sobre redirecionamento
  • Tipo da requisição
  • Cabeçalho da requisição

Se compararmos como XMLHttpRequest as informações serão as seguinte:

onabort: null onerror: null onload: null onloadend: null onloadstart: null onprogress: null onreadystatechange: null ontimeout: null readyState: 4 response: "{↵ "name":"fellyph",↵ "sobrenome": "cintra",↵ "blog": "https://blog.fellyph.com.br"↵}" responseText: "{↵ "name":"fellyph",↵ "sobrenome": "cintra",↵ "blog": "https://blog.fellyph.com.br"↵}" responseType: "" responseURL: "http://127.0.0.1:5500/dados.json" responseXML: null status: 200 statusText: "OK" timeout: 0 upload: XMLHttpRequestUpload {onloadstart: null, onprogress: null, onabort: null, onerror: null, onload: null, …} withCredentials: false

Como podemos ver temos a informações de requisições, respostas e eventos tudo num lugar só, isso dificulta o tratamento da informação e realiza um consumo de dados desnecessário para o usuário.

Na requisição anterior caso queiramos tratar os dados da resposta fazemos da seguinte forma:

fetch('/dados.json') .then(response => response.json()) .then(data => console.log(data));

Para o tratamento de erros realizamos da seguinte forma, adicionamos um catch na chamada de nosso catch:

fetch('/dados.json') .then(response => response.json()) .then(data => console.log(data)); .catch(error => { console.log('Algo de errado aconteceu:', error ) });

Tipos de requisição

A fetch api pode realizar requisições para diferentes tipos de dados, como texto, images, como processar arquivos de text linha a linha. Além disso podemos enviar informações e realizar upload de arquivos.

Um exemplo de um envio de um arquivo json:

const data = { name: 'fellyph', sobrenome: 'cintra', blog: 'https://blog.fellyph.com.br' }; fetch('/usuario', { method: 'POST', // ou 'PUT' headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }) .then(response => response.json()) .then(data => { console.log('Success:', data); }) .catch((error) => { console.error('Error:', error); });

Suporte

Fetch API é suportada por 95% dos browsers, os browsers que não possuem suporte:

  • Internet Explorer 11
  • Edge 13 e inferior

Podemos verificar o suporte da seguinte forma:

if (window.fetch) { // realizamos o request com fetch } else { // utilizamos um fallback }

XMLHttpRequest vs. Fetch API

Eles surgiram em tempos diferentes, por muito tempo XMLHttpRequest deu conta do recado mas com a evolução das aplicações web começamos a ter a necessidade de multiplas requisições, foi que então começamos a ter problemas com correntes de callbacks requisições a diferentes serviços.

Para solucionar esse problema tivemos a introdução de promises e junto a ela tivemos a inclusão de novas APIs que começaram a utilizar esse recurso como Fetch API, Service Worker entre outras Web APIs.

Podemos dizer que Fetch API é a evolução da XMLHttpRequest. Apesar da estrutura ter mudado drasticamente elas possuem o mesmo objetivo solucionar o problema com requisições assíncronas. Além de ser extremamente poderosa.

Para mais tutoriais sobre desenvolvimento web acesse a página da categoria tutoriais.

Categorias
HTML5 Tutoriais

Introdução a imagens responsivas com srcset

Imagens são responsáveis pela maior parte do carregamento de bytes de uma aplicação web em geral. Não podemos evitar-las pois imagens são um ponto crucial de nossa aplicação. Mas temos alternativas para reduzir esse impacto, por exemplo, geralmente nossa aplicação web possui diferentes usuários com diferentes tamanhos de tela.

Imagine o seguinte cenário, em seu website você possui uma imagem de apresentação do seu produto com 1600 pixels, na versão desktop não temos nenhum problema mas o usuário em um dispositivo móvel carrega essa mesma imagem numa tela de 340px. Porque então não servimos diferentes tamanhos de imagens para diferentes tamanhos de tela?

Imagens responsivas é uma parte do design responsivo que pode resolver essa questão. com elas podemos prover diferentes tamanhos de imagens para tamanhos de telas específicos também conseguimos fazer direção de arte disponibilizando recortes específicos da mesma imagem.

Criando imagens responsivas

Com HTML temos algumas alternativas para servir diferentes tamanhos de imagens de acordo com sua resolução de tela.

  • Media queries
  • srcset
  • Tag picture

Srcset

Para esse post iremos trabalhar com a propriedade srcset. Ela permite mais de uma resolução possível das mesma image separadas por vírgula, cada valor dessa lista representa um caso possível ela pode ser atrelada a uma condicional definida pela propriedade sizes.

Vamos usar como exemplo uma tag img tradicional para os nossos testes:

<img src="/imgs/amp-workshop-dublin-1024w.jpg" alt="AMP Workshop Dublin">
Imagem utilizada no exemplo

Agora vamos adicionar os diferentes tamanhos para a imagem:

  • amp-workshop-dublin-640w.jpg
  • amp-workshop-dublin-960w.jpg
  • amp-workshop-dublin-1024w.jpg

Agora que temos os tamanhos definidos vamos alterar nossa img tag:

<img srcset="./imgs/amp-workshop-dublin-640w.jpg 640w, ./imgs/amp-workshop-dublin-960w.jpg 960w, ./imgs/amp-workshop-dublin-1024w.jpg 1024w" src="/imgs/amp-workshop-dublin-1024w.jpg" alt="AMP Workshop">

Com o código acima especificamos o tamanho das imagens nesse caso o browser decidirá que imagem ira carregar de acordo com a densidade de pixels e tamanho de tela(janela). No caso das imagens por densidade de pixel definimos o valor que representa a densidade ao no lugar da largura:

<img srcset="./imgs/amp-workshop-dublin-640w.jpg 1x, ./imgs/amp-workshop-dublin-1600w.jpg 2x" src="./imgs/amp-workshop-dublin-640w.jpg" alt="AMP Workshop">

Muitas vezes queremos fazer direção de arte, por exemplo, queremos exibir em mobile e table uma imagem com largura cheia e para desktop layout duas colunas, nesse caso a imagem não sera proporcional a largura da tela precisamos especificar para o browser qual tamanho de imagem queremos nesse caso iremos fazer a utilização da propriedade size.

Size

Com a propriedade size tomamos o controle e definimos que imagens iremos carregar, passando uma media query e o tamanho que identifica a imagem. Vamos colocar tudo junto para isso vamos criar uma página para o exercício:

Versão Desktop
versão tablet e mobile

A imagem do header contém o código que virmos anteriormente agora vamos adicionar o código para os thumbnails, quando o usuário estiver no desktop iremos exibir o layout em duas colunas e quando ele estiver no tablet e desktop iremos exibir em uma coluna. O controle sobre a quantidade de colunas vem do css, mas com a tag image vamos garantir o carregamento da imagem correta.

Para o thumb vamos adicionar o seguinte código:

<img srcset="./imgs/thumb-480w.jpg 480w, ./imgs/thumb-960w.jpg 960w" sizes="(max-width: 480px) 480px, (max-width: 960px) 960px, (min-width: 961px) 480px" src="./imgs/thumb-960w.jpg" class="thumb-post" alt="post thumb">

No código acima temos dois tamanhos 480px e 960px, na propriedade sizes definimos as medias queries que iremos trabalhar nesse caso mobile(480px max), tablet(960px max), desktop(961px em diante).

O código completa fica da seguinte forma:

HTML

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Responsive images</title> <link rel="stylesheet" href="style.css"> </head> <body> <section> <header> <img srcset="./imgs/amp-workshop-dublin-640w.jpg 640w, ./imgs/amp-workshop-dublin-960w.jpg 960w, ./imgs/amp-workshop-dublin-1024w.jpg 1024w, ./imgs/amp-workshop-dublin-1600w.jpg 1600w" src="/imgs/amp-workshop-dublin-1024w.jpg" alt="AMP Workshop"> <h1 class="page-title">Fellyph Cintra</h1> </header> <div class="posts"> <article class="post"> <img srcset="./imgs/thumb-480w.jpg 480w, ./imgs/thumb-640w.jpg 640w, ./imgs/thumb-960w.jpg 960w" sizes="(max-width: 480px) 480px, (max-width: 640px) 640px, (max-width: 960px) 960px, (min-width: 961px) 480px" src="./imgs/thumb-960w.jpg" class="thumb-post" alt="post thumb"> <div class="content"> <h2 class="title">Post title</h2> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus nostrum, soluta earum dolorem magnam eos porro, aliquam ex temporibus tempore dolor suscipit quibusdam sint impedit eveniet non placeat dicta nesciunt.</p> </div> </article> <article class="post"> <img srcset="./imgs/thumb-480w.jpg 480w, ./imgs/thumb-640w.jpg 640w, ./imgs/thumb-960w.jpg 960w" sizes="(max-width: 480px) 480px, (max-width: 640px) 640px, (max-width: 960px) 960px, (min-width: 961px) 480px" src="./imgs/thumb-960w.jpg" class="thumb-post" alt="post thumb"> <div class="content"> <h2 class="title">Post title</h2> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus nostrum, soluta earum dolorem magnam eos porro, aliquam ex temporibus tempore dolor suscipit quibusdam sint impedit eveniet non placeat dicta nesciunt.</p> </div> </article> <article class="post"> <img srcset="./imgs/thumb-480w.jpg 480w, ./imgs/thumb-640w.jpg 640w, ./imgs/thumb-960w.jpg 960w" sizes="(max-width: 480px) 480px, (max-width: 640px) 640px, (max-width: 960px) 960px, (min-width: 961px) 480px" src="./imgs/thumb-960w.jpg" class="thumb-post" alt="post thumb"> <div class="content"> <h2 class="title">Post title</h2> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus nostrum, soluta earum dolorem magnam eos porro, aliquam ex temporibus tempore dolor suscipit quibusdam sint impedit eveniet non placeat dicta nesciunt.</p> </div> </article> <article class="post"> <img srcset="./imgs/thumb-480w.jpg 480w, ./imgs/thumb-640w.jpg 640w, ./imgs/thumb-960w.jpg 960w" sizes="(max-width: 480px) 480px, (max-width: 640px) 640px, (max-width: 960px) 960px, (min-width: 961px) 480px" src="./imgs/thumb-960w.jpg" class="thumb-post" alt="post thumb"> <div class="content"> <h2 class="title">Post title</h2> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus nostrum, soluta earum dolorem magnam eos porro, aliquam ex temporibus tempore dolor suscipit quibusdam sint impedit eveniet non placeat dicta nesciunt.</p> </div> </article> <article class="post"> <img srcset="./imgs/thumb-480w.jpg 480w, ./imgs/thumb-640w.jpg 640w, ./imgs/thumb-960w.jpg 960w" sizes="(max-width: 480px) 480px, (max-width: 640px) 640px, (max-width: 960px) 960px, (min-width: 961px) 480px" src="./imgs/thumb-960w.jpg" class="thumb-post" alt="post thumb"> <div class="content"> <h2 class="title">Post title</h2> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus nostrum, soluta earum dolorem magnam eos porro, aliquam ex temporibus tempore dolor suscipit quibusdam sint impedit eveniet non placeat dicta nesciunt.</p> </div> </article> <article class="post"> <img srcset="./imgs/thumb-480w.jpg 480w, ./imgs/thumb-640w.jpg 640w, ./imgs/thumb-960w.jpg 960w" sizes="(max-width: 480px) 480px, (max-width: 640px) 640px, (max-width: 960px) 960px, (min-width: 961px) 480px" src="./imgs/thumb-960w.jpg" class="thumb-post" alt="post thumb"> <div class="content"> <h2 class="title">Post title</h2> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus nostrum, soluta earum dolorem magnam eos porro, aliquam ex temporibus tempore dolor suscipit quibusdam sint impedit eveniet non placeat dicta nesciunt.</p> </div> </article> </div> <footer class="footer"> Tutorial by Fellyph </footer> </section> </body> </html>

CSS

.page-title, .posts, .footer { width: 1024px; margin: 1em auto; max-width: 100%; } .page-title { text-align: center; font-size: 3em; border-bottom: 3px solid black; padding-bottom: 1em; } .posts { display: flex; flex-wrap: wrap; } .post { width: 100%; margin: 1em; background: white; display: inline-block; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.26); border: 1px solid #eee; box-sizing: border-box; } .thumb-post { max-width:100%; height: auto; } .content { padding: 1em; } .footer { border-top: 3px solid black; padding-top: 1em; text-align: center; } @media (min-width: 960px) { .post { width: calc(50% - 2em); } }

O post serviu como introdução a imagens responsivas temos outros caminhos para trabalhar com imagens responsivas e vamos abordar em mais posts, se quiser conferir mais tutoriais confira a página da categoria tutoriais ou deixe um comentário.

Categorias
HTML5 JavaScript Tutoriais

localStorage: Armazenando dados com JavaScript

Esse post é um dos posts mais visualizados do blog, foi criado em 2013 mas vou dar uma atualizada no que aconteceu de lá pra cá. Armazenamento local com localStorage é umas das features mais úteis do HTML5 podemos utilizar para uma infinidade de ações agora com Progressive Web Apps esse recursos voltou a ficar evidência.

Ela permite o armazenamento de strings com chave e valor e considerado cookies é uma forma mais segura de armazenamento, mas ela não tem acesso através de Service Worker. Mas é um recurso nativo no browser de implementação simples que não necessita de plugin ou library externa.

Para aplicações mas complexas temos a opção de utilizar o IndexedDB. Mas um dos pontos fortes do localStorage é o suporte comparado a outras APIs ele é suportado por 93% dos browsers:

LocalStorage é um dos recursos do DOM Storage com ele podemos armazenar dados apenas em formato de texto, mas claro podemos contornar essa limitação. Vamos primeiro exemplo básico de armazenamento.

localStorage.setItem("key_da_propriedade","Valor armazenado");

No código acima adicionamos uma variável com nome “key_da_propriedade” que irá armazenar “Valor armazenado”. Quando esse código for armazenado pelo browser o usuário pode sair da aplicação, continuar navegando em outras aplicações quando ele voltar para nossa aplicação o dado continuará lá. Esse recurso será útil caso utilizarmos uma versão offline de nossa aplicação com PWA.

Para visualizar a informação que foi salva, no Google Chrome para abrir o developer Tools vamos em “menu > View > developer > developer tools”, tecla de atalho ctrl+shift+i ou simples clique o com botão direito do mouse e selecionamos a opção “inspect”, com developer tools aberto vamos na aba Application, na sessão storage vemos a opções disponíveis. Quando clicamos em Local Storage vemos as aplicações separadas por url, meu teste estou rodando em localhost, clicando na url da nossa aplicação vemos os dados armazenados:

Agora que sabemos que a informação foi salva vamos resgatar esse valor com o seguinte código:

const minha_propriedade = localStorage.getItem('key_da_propriedade'); alert("Valor:" + minha_propriedade);

No código acima passamos a função getItem e como parâmetro passamos a key da informação que queremos resgatar, na linha seguinte apenas executamos um alert para exibir a informação.

Caso queria remover essa informação do localStorage utilizamos o seguinte código:

localStorage.removeItem('key_da_propriedade');

Agora vamos para um exemplo prático aplicando essas três funções que virmos acima, no caso: salvar, ler e excluir os dados no localStorage.

<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>LocalStorage tutorial</title> <script> window.addEventListener('load',() => { if(localStorage.getItem('name')){ sayMyName() }else{ whatsYourName() } }); function whatsYourName(){ document.body.innerHTML = ''; // criando um input para cadastrar o nome; const inputName = document.createElement('input'); inputName.type = 'text'; inputName.placeholder = 'Digite seu nome'; inputName.id = 'nome'; document.body.appendChild(inputName); // criando saveButton const saveButton = document.createElement('button'); saveButton.innerHTML = 'Salvar'; document.body.appendChild(saveButton); // adicionando listener para salvar a informação saveButton.addEventListener('click', saveName); } function sayMyName(){ document.body.innerHTML = ''; // criando mensagem const welcomeMessage = document.createElement('h1'); welcomeMessage.innerText = 'Olá' + localStorage.getItem("name"); // criando removeButton const removeButton = document.createElement('button'); removeButton.innerHTML = 'Excluir'; document.body.appendChild(removeButton); // adicionando o listener para remover informação removeButton.addEventListener('click',removeName); } function removeName(){ if(localStorage.getItem('name')){ localStorage.removeItem('name'); whatsYourName() } } function saveName(){ var nome = document.getElementById('nome').value; localStorage.setItem('name', nome) sayMyName(); } </script> </head> <body> </body> </html>

O Exemplo acima resumindo rapidamente ele verifica se tem o dado com a key “name” no localStorage, caso tenha, exibe o nome. Caso contrário adiciona um input para cadastrar o dado. Quando o nome é exibido também adicionamos um button para chamar a função de excluir os dados. Nos Exemplos anteriores apenas adicionamos e removemos algumas strings, para trabalhar com objetos no localStorage precisamos usar a Classe JSON, com ela é possível fazer a ponte entre textos e objetos.

window.addEventListener('load',() => { if(localStorage.getItem('user')){ const texto = localStorage.getItem("user"); const objeto = JSON.parse(texto); document.body.innerHTML = `nome: <strong>${objeto.name}</strong> email: <strong>${objeto.email}<strong>`; }else{ const user = { name: 'Fellyph', email: 'fellyph@fellyph.com.br' } const userString = JSON.stringify(user); localStorage.setItem('user',userString); document.body.innerHTML = 'dados salvos'; } });

No código acima focamos apenas no javaScript, verificamos se existe um dado com uma key “user”, caso exista ele resgata a informação na variável “texto” em seguida convertemos o texto em objeto utilizando a método parse da classe JSON, assim temos um objeto como retorno e adicionamos o conteúdo no body do nosso arquivo, no código acima utilizamos string literals para concatenar o nosso conteúdo.

Caso o usuário não tenha nenhuma informação salva com a key “user”, o código irá criar um objeto e em seguida converter em texto, assim temos a possibilidade de salvar os dados no em nosso localStorage. Esse exemplo não é muito funcional apenas exemplifica como é salvar um objeto em nosso localStorage.

Agora vamos partir para um exemplo mais funcional, vamos montar uma todo-list utilizando localStorage, desta vez vamos separar o código no arquivo todo-list.html, js/app.js e css/style.css. O exemplo a seguir utiliza ES2015 caso queira dar uma olhada no exemplo em EcmaScript 5 só da uma olhada no gitHub no seguinte link: https://github.com/fellyph/Tutorial-bbUI/tree/master/localstorage

Nosso HTML:

<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Todo List</title> <link rel="stylesheet" href="css/style.css"> <script src="js/app.js"></script> </head> <body> <h1>todo-list</h1> <div id="tasks-output"></div> <form id="form-task"> <input type="text" name="descricao" placeholder="adicione uma nova task" required> <input type="submit" value="Salvar"> </form> </body> </html>

Nosso CSS:

#tasks-output li { cursor: pointer; } #form-task { border-top: 1px solid #ddd; padding: 10px 0; margin: 10px 0; } #tasks-output li[data-done="true"] { text-decoration: line-through; }

Detalhe para o ultimo seletor que trata quando o item da lista possui o atributo data-done igual a true, ele vai adicionar uma linha sobre o texto.

let todoList; let todoOutput; function formatDate(date) { // formata a data para o formato DD/MM/YYYY const time = new Date(date); return `${time.getDate()}/${time.getMonth()}/${time.getFullYear()}`; } function showList() { // mostra a lista de todo if (todoList.length > 0) { const htmlTemp = `<ul> ${todoList.map(todoItem => `<li data-id="${todoItem.id}" data-done="${todoItem.done}">${todoItem.descricao} - ${formatDate(todoItem.date)}</li>` )} </ul><button>Limpar tarefas realizadas</button>`; todoOutput.innerHTML = htmlTemp; } else { todoOutput.innerHTML = 'Nenhuma tarefa cadastrada'; } } function saveList() { // converte os dados em string e salva no local storage localStorage.setItem('tasks', JSON.stringify(todoList)); } function clearList() { // varre a lista a procura de tarefas realizadas for (let i = 0; i < todoList.length; i += 1) { if (todoList[i].done === 'true') { // remove 1 elemento na posição i; todoList.splice(i, 1); // voltando o indice no array para validar novamente a lista i = 0; } else { todoList[i].id = i; } } showList(); saveList(); } function clickList(e) { // somente fazer algo quando clicar em um item li if (e.target.localName === 'li') { e.target.dataset.done = !e.target.dataset.done === 'true'; todoList[e.target.dataset.id].done = e.target.dataset.done; saveList(); } else if (e.target.localName === 'button') { clearList(); } } function onSubmit(e) { const task = {}; // pego o valor cadastrado no primeiro input do meu form task.descricao = e.target[0].value; task.date = new Date(); task.id = todoList.length; task.done = 'false'; // adicionando a task na lista todoList.push(task); saveList(); showList(); // utiliza o preventDefault para evitar do form realizar o reload da página e.preventDefault(); } window.addEventListener('load', () => { // guarda em uma variável o elemento tasks-output todoOutput = document.getElementById('tasks-output'); if (localStorage.getItem('tasks')) { todoList = JSON.parse(localStorage.getItem('tasks')); showList(); } else { todoList = []; } if (todoList.length === 0) { todoOutput.innerHTML = 'Nenhuma tarefa cadastrada'; } // adiciona o listener para o evento submit, utilizei form para usar o required do input HTML document.getElementById('form-task').addEventListener('submit', onSubmit); todoOutput.addEventListener('click', clickList); });

Tentei comentar os pontos mais importantes do código, na listagem utilizo um map para montar nossa lista caso não esteja familiarizado só conferir a versão anterior desse código no git ou conferir os posts relacioanos a ecmaScript 2015, mas o código acima o usuário pode cadastrar as tasks que serão vinculadas uma data, quando o usuário clica no item da lista a propriedade “data-done” muda entre true ou false. caso o usuário queira limpar sua listas de task clica no botão “Limpar tarefas…” o código irá varrer da lista os itens com valor da propriedade data-done igual a true e retirar da lista.

Além do LocalStorage temos o SessionStorage, a diferença entre os dois é que o sessionStorage como o nome sugere ele guarda as informações apenas na seção ou seja, quando o aplicação é finalizada a informação é removida.

Fechamos por aqui e até o próximo tutorial, para saber mais como acessar recursos do device confira a página da categoria PWA.

Categorias
HTML5 JavaScript Tutoriais

Observe a orientação do device com JavaScript

Com Javascript podemos observar a orientação do device, também é possível realizar este tratamento com CSS, mas isso fica restrito a questões visuais, caso queria realizar algum tratamento lógico em nossa aplicação podemos utilizar JavaScript.

Temos “N” possibilidades de acompanhar a orientação do device com JavaScript, nesse post vou mostrar duas maneiras bem simples com JavaScript, a primeira observando o evento ‘orientationchange’, para isso adicionamos um listener em em nossa janela com o seguinte código:

window.addEventListener('orientationchange', function(){ // faça alguma coisa });

O código acima acompanha quando a orientação do device muda, mas como sabemos que o usuário está em landscape ou portrait, para isso pegamos a orientação do device, com window.orientation essa propriedade retorna a rotação do device:

window.addEventListener('orientationchange', function(){ switch(window.orientation) { case -90: case 90: alert('landscape'); break; default: alert('portrait'); break; } });

Com as informações sobre a rotação do device sabemos que 90 ou -90 o device está em landscape e diferente disso portrait. No código acima foi disparado somente um alert, mas poderíamos chamar alguma outra função, editar uma classe de um elemento ou adicionar uma conteúdo específico para o usuário.

matchMedia

A segunda opção seria com matchMedia ela faz parte da Web API Interface com ela podemos validar media queries por exemplo:

if (window.matchMedia("(min-width: 360px)").matches) { /* O view port tem pelo menos 360px de largura */ } else { /* o view port tem menos de 360px de largura */ }

Podemos validar qualquer media query no CSS3 temos uma regra para verificar a orientação vamos utilizar ela para pegar o orientação do nosso device:

function handleOrientationChange(mediaQuerie) { if (mediaQuerie.matches) { alert("portrait") } else { alert("landscape") } } /* Criamos uma variável para armazenar a media querie */ var mediaQuerie = window.matchMedia("(orientation: portrait)"); /* Adicionamos um listenr para acompanhar a mudança de orientação */ mediaQuerie.addListener(handleOrientationChange);

Temos ai duas opções para verificar a orientação do device com JavaScript. Para saber sobre Progressive web apps eu tenho um curso sobre o assunto. Tenho vídeos gratuítos sobre Desenvolvimento Mobile Web em meu canal do Youtube. Caso tenha qualquer dúvida só deixar um comentário.

Categorias
HTML5 Mobile Dev Group São Paulo

Limites do LocalStorage

Sempre quando falo de localStorage as pessoas me perguntam: “Qual o limite do LocalStorage?” sempre respondia o valor padrão de 5MB, mas esse valor é apenas uma especificação da W3C. Segundo ela:

A mostly arbitrary limit of five megabytes per origin is suggested. Implementation feedback is welcome and will be used to update this suggestion in the future.

Ela sugere(não é obrigatório) que o espaço reservado por origem seja de 5MB e no futuro esse limite pode ser atualizado, a origem citada anteriormente no caso é por cada domínios. As variações a1.exemplo.com , a2.exemplo.com , a3.exemplo.com são considerada a mesma origem.

Em nosso caso o espaço disponível para o localStorage em aplicações em HTML5 na plataforma BlackBerry 10 é de 25MB, para armazenamento de texto 25MB é muita informação. Caso chegue ao limite do armazenamento será levantada uma exceção : QUOTA_EXCEEDED_ERR.

Caso queria saber o limite de armazenamento do seu browser, existe esse teste online: http://arty.name/localstorage.html

Segundo o site citado acima podemos modificar o limite de armazenamento no Opera, Firefox e no Chrome/Safari/IE