1. Colocando conteúdo com HTML
o
tag é a tag HTML semântica usada para exibir dados em uma página da web. Estaremos colocando nossos
tag dentro de um div de contêiner de tabela que nos permitirá incluir algum estilo responsivo em CSS.
Nossa tabela conterá o cabeçalho da tabela, thead
e conteúdo da tabela, tbody
Tag. No cabeçalho da nossa tabela, incluiremos botões em cada th
célula que será usada para lidar com a funcionalidade de classificação. As células para o conteúdo da tabela serão adicionadas com JavaScript usando os dados de nossa resposta JSON.
2. Fazendo uma tabela responsiva com CSS
Um dos problemas mais comuns de trabalhar com tabelas HTML é a falta de capacidade de resposta. A tabela pode ter um problema de sobreposição de células ou ultrapassar os limites da página inteira.
Podemos nos livrar desses problemas colocando a tabela em um contêiner de tabela com a largura da página inteira com uma propriedade de rolagem de estouro. Dessa forma, a tabela é sempre tão larga quanto a página inteira e não há necessidade de reduzir as células graças ao estouro rolável. Também incluiremos propriedades de largura mínima em nossas células de tabela para evitar quebra de texto.
Este é o CSS necessário para tornar nossa tabela rolável:
.table-container {
width: 100%;
overflow: scroll;
}
table {
width: 100%;
}
Podemos então adicionar o resto do nosso estilo:
.table-container {
margin: auto;
max-width: 1200px;
min-height: 100vh;
overflow: scroll;
width: 100%;
}
table {
border-collapse: collapse;
width: 100%;
}
thead tr {
border-bottom: 1px solid #ddd;
border-top: 1px solid #ddd;
height: 1px;
}
th {
font-weight: bold;
height: inherit;
padding: 0;
}
th:not(:first-of-type) {
border-left: 1px solid #ddd;
}
th button {
background-color: #eee;
border: none;
cursor: pointer;
display: block;
font: inherit;
height: 100%;
margin: 0;
min-width: max-content;
padding: 0.5rem 1rem;
position: relative;
text-align: left;
width: 100%;
}
tbody tr {
border-bottom: 1px solid #ddd;
}
td {
padding: 0.5rem 1rem;
text-align: left;
}
3. Colocando dados JSON em uma tabela HTML
Para este exemplo, trabalharemos com uma resposta JSON analisada simulada. É assim que nossos dados se parecem:
const response = {
"pokedata": [
{
"name": "Bulbasaur",
"type": "Grass",
"hp": 45,
"attack": 49,
"defense": 49,
"spAttack": 65,
"spDefense": 65,
"speed": 45,
"total": 318
},
...
]
}
Nós estaremos colocando os dados dentro do nosso
tag para que possamos direcionar esse elemento em JavaScript;
const tableContent = document.getElementById("table-content")
O conteúdo da linha será baseado em cada objeto dentro do nosso response.pokedata
. Vamos definir uma função para criar uma nova linha com base nos dados do objeto:
const createRow = (obj) => {
const row = document.createElement("tr");
const objKeys = Object.keys(obj);
objKeys.map((key) => {
const cell = document.createElement("td");
cell.setAttribute("data-attr", key);
cell.innerHTML = obj[key];
row.appendChild(cell);
});
return row;
};
Nesta função, usamos o Object.keys()
método para obter as chaves do objeto como uma matriz. É assim que o valor retornado se parece:
Depois de obter a matriz das chaves do objeto, percorremos cada chave usando o .map()
método. O método map executa a função que passamos para ele em cada item do array.
Nesta função map, estamos criando uma nova célula para cada item na matriz e definindo a célula innerHTML como o valor da chave do objeto correspondente.
Por fim, anexamos a célula que criamos à linha definida no início da função usando o .appendChild()
método.
Agora que temos nossa função de criação de linha, definiremos uma função para percorrer o response.pokedata
array e anexar cada nova linha ao nosso tableContent
elemento.
const getTableContent = (data) => {
data.map((obj) => {
const row = createRow(obj);
tableContent.appendChild(row);
});
};
Nós vamos passar o nosso getTableContent
função em um ouvinte de eventos para que o conteúdo seja adicionado à tabela assim que a página for carregada.
window.addEventListener("load", () => {
getTableContent(response.pokedata);
});
4. Classificando dados com JavaScript
Agora que criamos nossa tabela, vamos adicionar a funcionalidade de classificação. Em nosso HTML, incluímos botões nas células de cabeçalho que tinham as chaves do objeto como seu id. Vamos segmentar esses botões agora:
const tableButtons = document.querySelectorAll("th button");
Queremos lidar com a classificação dos dados de acordo com qual botão é clicado e também incluir um recurso que alterna a direção de classificação (ascendente ou descendente) quando o botão é clicado.
Podemos usar o .sort()
método para lidar com a classificação dos dados em nosso response.pokedata
variedade. O método sort recebe uma função comparando dois parâmetros diferentes e os classifica de acordo com a resposta da função:
compareFunction(a, b)
valor de retorno
ordem de classificação
> 0
ordenar a
depois b
< 0
ordenar a
antes da b
=== 0
manter a ordem original de a
e b
Fonte: MDN
Outra coisa a ser observada sobre o método de classificação é que ele altera a matriz original em que opera. Isso significa que ele altera o valor do nosso array original.
Podemos evitar a mutação de nosso array original usando a sintaxe de propagação […]
Agora podemos criar nossa função de classificação. Esta é a aparência da lógica para nossa função de classificação:
- Limpe o conteúdo no elemento tableContent
- Classifique os dados de acordo com o parâmetro e direção selecionados
- Anexe os dados classificados ao nosso tableContent usando o
getTableContent
função
const sortData = (data, param, direction = "asc") => {
tableContent.innerHTML = '';
const sortedData =
direction == "asc"
? [...data].sort(function (a, b) {
if (a[param] < b[param]) {
return -1;
}
if (a[param] > b[param]) {
return 1;
}
return 0;
})
: [...data].sort(function (a, b) {
if (b[param] < a[param]) {
return -1;
}
if (b[param] > a[param]) {
return 1;
}
return 0;
});
getTableContent(sortedData);
};
Nossa função de classificação recebe três parâmetros:
-
data
: o array a ser ordenado
-
param
: o valor que está sendo usado para classificar a matriz
-
direction
: ordena o array em ordem crescente ou decrescente. O valor do parâmetro padrão é definido como “asc”.
Limpamos o conteúdo em nosso elemento tableContent definindo o innerHTML como uma string em branco. Usamos então o .sort()
método e direction
parâmetro para determinar como os dados devem ser classificados. Invertemos a função de comparação para classificar em ordem decrescente. Usar a função compare desta forma nos permite classificar os dados independentemente do valor do tipo (string, integer, float etc)
Por fim, passamos a sortedData
como o novo valor em nosso conteúdo de tabela.
Agora, passaremos nossa função de classificação para um ouvinte de evento de clique para nossos botões de tabela e também manipularemos a alternância da direção de classificação.
window.addEventListener("load", () => {
getTableContent(response.pokedata);
[...tableButtons].map((button) => {
button.addEventListener("click", (e) => {
if (e.target.getAttribute("data-dir") == "desc") {
sortData(response.pokedata, e.target.id, "desc");
e.target.setAttribute("data-dir", "asc");
} else {
sortData(response.pokedata, e.target.id, "asc");
e.target.setAttribute("data-dir", "desc");
}
});
});
});
Nesta função, lidamos com a alternância definindo um data-dir
atributo em nossos botões para determinar a direção de classificação. Também atualizaremos nosso CSS para exibir um ícone ao lado do botão, dependendo da direção de classificação:
th button::after {
position: absolute;
right: 0.5rem;
}
th button[data-dir="asc"]::after {
content: url("data:image/svg+xml,%3Csvg xmlns="https://www.w3.org/2000/svg" width="8" height="8"%3E%3Cpolygon points="0, 0 8,0 4,8 8" fill="%23818688"/%3E%3C/svg%3E");
}
th button[data-dir="desc"]::after {
content: url("data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="8" height="8"%3E%3Cpolygon points="4 0,8 8,0 8" fill="%23818688"/%3E%3C/svg%3E");
}
Nós não queremos que os ícones apareçam em todos os botões previamente clicados, então vamos definir um resetButtons
função que remove o atributo data-dir em qualquer botão que não seja o botão clicado.
const resetButtons = (event) => {
[...tableButtons].map((button) => {
if (button !== event.target) {
button.removeAttribute("data-dir");
}
});
};
Passaremos essa função para nosso ouvinte de eventos de botão para redefinir os botões anteriores sempre que um novo botão for clicado
window.addEventListener("load", () => {
getTableContent(response.pokedata);
[...tableButtons].map((button) => {
button.addEventListener("click", (e) => {
resetButtons(e);
if (e.target.getAttribute("data-dir") == "desc") {
sortData(response.pokedata, e.target.id, "desc");
e.target.setAttribute("data-dir", "asc");
} else {
sortData(response.pokedata, e.target.id, "asc");
e.target.setAttribute("data-dir", "desc");
}
});
});
});
Conclusão
E com isso, terminamos! Criamos uma tabela classificável usando apenas JavaScript vanilla!