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 esta API, ela permite o armazenamento de strings com chave e valor e considerado cookies é uma forma mais segura de armazenamento. Recurso nativo no browser não necessita de plugin ou library externa.

Para aplicações mas complexas temos a opção de utilizar o IndexDB ou a mais nova opção de armazenamento KV Storage agora com suporte na versão 74 do Google Chrome. 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

Categorias
HTML5 Tutoriais

Crie accordion sem javascript

 

<details> é uma tag que podemos encapsular de desencapsular um determinado conteúdo, para obter informações adicionais sobre uma determinada informação, resumindo um accordion. Para trabalhar com esse recurso além da elemento <details>, precisamos do elemento <summary>, ele deve ser incluso como filho do <details>. Com este elemento conseguimos prover uma legenda ou um capítulo para o conteúdo restante, ele também será responsável por clicarmos nele e o conteúdo ser exibido.

O elemento <details> tem um atributo chamado open, ele define se o conteúdo será inicialmente visível ou não. Por default essa propriedade é false. Com esse recurso produz uma interatividade sem nenhum javascript.

Vamos ao código:

<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title> Details </title>
	<style>
		details{
			border-radius:3px;
			padding:5px;

		}
		summary{
			border-radius:3px;
			border:1px solid #ddd;
			padding:5px;
			outline:none; 
		}

		details[open]{
			background:#ddd;
			border:1px solid #333;
		}
		details[open] summary{
			border:none;
		}
	</style>
</head>
<body>

<details open>
	<summary>Autor</summary>
	<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nulla, nemo, dolores numquam fugit ipsum velit eaque repudiandae ea libero est.</p>
</details>

<details>
	<summary>Livros</summary>
	<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nulla, nemo, dolores numquam fugit ipsum velit eaque repudiandae ea libero est.</p>
</details>

<details>
	<summary>Jobs</summary>
	<p> Consequuntur, consequatur ut earum dolore placeat doloribus in voluptatem culpa.</p>
</details>
</body>
</html>

No código acima adicionei um CSS para dar um acabamento visual, nele vou fazer duas observações, o summary por default quando clicado exibe um highlight para remove-lo basta adicionar a propriedade “outline:none; “. A segunda observação é referente a como estilizar um item quando ele está ativo, para isso utilizamos o seguinte seletor “details[open]”. No código HTML o primeiro item tem a propriedade open setada assim ele inicia com seu conteúdo exibido. e o resultado será o seguinte:

 

Um recurso simples que economiza o uso de Javascript. O código desse tutorial você pode encontrar no meu gitHub no seguinte link: https://github.com/fellyph/Tutorial-bbUI/tree/master/details