Neste tutorial, recriaremos a página do autor do Tuts+ e implementaremos um componente de filtro para nos permitir filtrar artigos com base em sua categoria.
1. Layout e estilo
Para esta demonstração, clonaremos a página do autor para que a marcação e o estilo sejam obtidos diretamente do site ativo. Os principais componentes nos quais focaremos são os filtros e as postagens exibidas, então criaremos um
e direcioná-los em JavaScript.
Estilizaremos esses contêineres e seu conteúdo em CSS:
1
.posts-container {
2
display: flex;
3
flex-wrap: wrap;
4
}
5
6
.post {
7
position: relative;
8
width: 300px;
9
margin-right: 30px;
10
margin-bottom: 40px;
11
border: 1px solid #e1e8ed;
12
border-radius: 4px;
13
margin-top: 13px;
14
min-height: 300px;
15
height: auto;
16
}
17
18
.filter-container {
19
display: flex;
20
flex-wrap: wrap;
21
justify-content: flex-start;
22
gap: 10px;
23
padding: 32px 0;
24
border-top: 1px solid #e4e4e4;
25
border-bottom: 1px solid #e4e4e4;
26
margin-bottom: 32px;
27
}
28
29
.filter-button {
30
transition: background-color 200ms, color 200ms;
31
background-color: transparent;
32
font: inherit;
33
cursor: pointer;
34
display: inline-block;
35
padding: 0 8px;
36
color: #717171;
37
border: 1px solid #9b9b9b;
38
border-radius: 25px;
39
font-size: 14px;
40
white-space: nowrap;
41
}
42
43
.filter-button:hover {
44
background-color: #f3f3f3;
45
color: #3a3a3a;
46
}
47
48
.filter-button.is-active {
49
background-color: #0085b6;
50
border-color: #0085b6;
51
color: #fff;
52
}
2. Obtendo dados da página usando a API Fetch
Nesta demonstração, usaremos a API Fetch para recuperar dados fictícios extraídos da página de autores do Tuts+ e armazenados em uma essência do Github.
Nossos dados fictícios têm o formato:
1
[
2
{
3
"https://webdesign.tutsplus.com/tutorials/title"https://webdesign.tutsplus.com/tutorials/: ""https://webdesign.tutsplus.com/tutorials/,
4
"https://webdesign.tutsplus.com/tutorials/link"https://webdesign.tutsplus.com/tutorials/: ""https://webdesign.tutsplus.com/tutorials/,
5
"https://webdesign.tutsplus.com/tutorials/image"https://webdesign.tutsplus.com/tutorials/: ""https://webdesign.tutsplus.com/tutorials/,
6
"https://webdesign.tutsplus.com/tutorials/categories"https://webdesign.tutsplus.com/tutorials/: [ ]
7
},
8
...
9
]
10
Este é o script para buscar dados do script:
1
fetch("https://webdesign.tutsplus.com/tutorials/https://gist.githubusercontent.com/jemimaabu/b89339c1b7e5f81f8737fb66a858b6fc/raw/cdded4a10dbc98858481b5aedbcce3f3026dc271/tutorials"
2
).then(async (response) => {
3
// handle response data
4
});
Depois de obter nossos dados buscados, podemos manipular os dados e anexá-los à página.
3. Anexando dados à página da Web
Para cada objeto em nossa resposta buscada, criaremos um post div que exibirá os dados na página. Primeiro, vamos definir nossas variáveis globais:
1
let postsData = ""https://webdesign.tutsplus.com/tutorials/;
2
const postsContainer = document.querySelector("https://webdesign.tutsplus.com/tutorials/.posts-container"https://webdesign.tutsplus.com/tutorials/);
Então vamos criar uma função createPost()
que manipulará o acréscimo de um novo div ao elemento posts-container. Nesta função, criamos um novo elemento div com o nome da classe post
e defina seu innerHTML como os dados que queremos exibir.
1
const createPost = (postData) => {
2
const { title, link, image, categories } = postData;
3
const post = document.createElement("https://webdesign.tutsplus.com/tutorials/div"https://webdesign.tutsplus.com/tutorials/);
4
post.className = "https://webdesign.tutsplus.com/tutorials/post"https://webdesign.tutsplus.com/tutorials/;
5
post.innerHTML = `
6
${link}" target="_blank">
7
${image}">
8
9
10
${title}
11
12
${categories
13
.map((category) => {
14
return "https://webdesign.tutsplus.com/tutorials/' + category + "https://webdesign.tutsplus.com/tutorials/"https://webdesign.tutsplus.com/tutorials/;
15
})
16
.join(""https://webdesign.tutsplus.com/tutorials/)}
17
18
19
`;
20
21
postsContainer.append(post);
22
};
Dentro de nosso post innerHTML, usamos o join("")
método em nosso categories.map()
para remover o símbolo ‘,’ incluído em cada array.
Agora podemos atualizar nossa função de resposta para chamar o createPost()
função uma vez que os dados foram buscados:
1
fetch("https://webdesign.tutsplus.com/tutorials/https://gist.githubusercontent.com/jemimaabu/b89339c1b7e5f81f8737fb66a858b6fc/raw/cdded4a10dbc98858481b5aedbcce3f3026dc271/tutorials"
2
).then(async (response) => {
3
postsData = await response.json();
4
postsData.map((post) => createPost(post));
5
});
4. Obter parâmetros de filtro da resposta
Como estamos usando JavaScript, podemos mapear nossa resposta para criar uma lista dinâmica de parâmetros de filtro. Vamos incluir as variáveis globais para nossos dados de filtro:
1
let filterData = ""https://webdesign.tutsplus.com/tutorials/;
2
const filterContainer = document.querySelector("https://webdesign.tutsplus.com/tutorials/.filter-container"https://webdesign.tutsplus.com/tutorials/);
Agora queremos escrever um script que classifique a matriz de categorias em cada objeto de resposta e retorne uma lista exclusiva. Podemos atualizar nosso objeto de resposta para lidar com a obtenção de uma lista exclusiva de parâmetros de filtro
1
fetch("https://webdesign.tutsplus.com/tutorials/https://gist.githubusercontent.com/jemimaabu/b89339c1b7e5f81f8737fb66a858b6fc/raw/cdded4a10dbc98858481b5aedbcce3f3026dc271/tutorials"
2
).then(async (response) => {
3
postsData = await response.json();
4
postsData.map((post) => createPost(post));
5
filterData = [
6
...new Set(
7
postsData
8
.map((post) => post.categories)
9
.reduce((acc, curVal) => acc.concat(curVal), [])
10
)
11
];
12
});
Quebrando o código para nosso filterData:
- Nós usamos
[... new Set]
para criar uma matriz de valores exclusivos. Set retorna um objeto de valores únicos e a sintaxe de propagação […] converte o objeto em um array.
- Mapeamos os postsData para obter a matriz de categorias de cada objeto post dentro da resposta de dados.
- Nós usamos o
.reduce()
método para combinar a matriz de categorias para cada objeto de postagem em uma matriz.
Assim que obtivermos nossa matriz de valores de filtro exclusivos das categorias de postagem, podemos criar uma função para anexar cada filtro à página. Criaremos um novo elemento de botão e definiremos o innerText e o className. Também definiremos um atributo “data-state” para lidar com a alteração do estado do botão quando clicado.
Cada botão de filtro terá um ouvinte de evento de clique definido para a função handleButtonClick, que será responsável por lidar com a lógica de filtragem.
1
const createFilter = (filter) => {
2
const filterButton = document.createElement("https://webdesign.tutsplus.com/tutorials/button"https://webdesign.tutsplus.com/tutorials/);
3
filterButton.className = "https://webdesign.tutsplus.com/tutorials/filter-button"https://webdesign.tutsplus.com/tutorials/;
4
filterButton.innerText = filter;
5
filterButton.setAttribute("https://webdesign.tutsplus.com/tutorials/data-state"https://webdesign.tutsplus.com/tutorials/, "https://webdesign.tutsplus.com/tutorials/inactive"https://webdesign.tutsplus.com/tutorials/);
6
filterButton.addEventListener("https://webdesign.tutsplus.com/tutorials/click"https://webdesign.tutsplus.com/tutorials/, (e) =>
7
handleButtonClick(e, filter)
8
);
9
10
filterContainer.append(filterButton);
11
};
E atualize nossa função de resposta para chamar o createFilter()
função:
1
fetch("https://webdesign.tutsplus.com/tutorials/https://gist.githubusercontent.com/jemimaabu/b89339c1b7e5f81f8737fb66a858b6fc/raw/cdded4a10dbc98858481b5aedbcce3f3026dc271/tutorials"
2
).then(async (response) => {
3
postsData = await response.json();
4
postsData.map((post) => createPost(post));
5
filterData = [
6
...new Set(
7
postsData
8
.map((post) => post.categories)
9
.reduce((acc, curVal) => acc.concat(curVal), [])
10
)
11
];
12
filterData.map((filter) => createFilter(filter));
13
});
5. Lidar com filtragem quando clicado
Agora que obtivemos nossos botões de filtro e dados iniciais, podemos definir uma função para lidar com a filtragem dos dados quando um botão é clicado. Isso pode ser feito usando o .filter()
método na matriz de dados de posts.
Esta é a lógica que usaremos para filtrar os dados:
- Criar uma função
handleFilterPosts()
que aceita um parâmetro de filtro
- Use o
.filter()
método para filtrar nossos dados de postagens com base no parâmetro de filtro
- Limpe todos os elementos em posts-container e anexe o novo FilterData ao container.
1
const handleFilterPosts = (param) => {
2
let filteredPosts = [...postsData].filter(post => post.categories.includes(param))
3
4
postsContainer.innerHTML = ""https://webdesign.tutsplus.com/tutorials/;
5
filteredPosts.map(post => createPost(post))
6
};
Queremos detectar quando um botão foi clicado e atualizar o estado do botão. Neste tutorial, alternaremos os botões para que, se clicados uma vez, o botão seja definido como ativo e se clicado novamente, o botão é definido como inativo.
1
const handleButtonClick = (e, param) => {
2
const button = e.target;
3
const buttonState = button.getAttribute("https://webdesign.tutsplus.com/tutorials/data-state"https://webdesign.tutsplus.com/tutorials/);
4
5
if (buttonState =="https://webdesign.tutsplus.com/tutorials/inactive"https://webdesign.tutsplus.com/tutorials/) {
6
button.classList.add("https://webdesign.tutsplus.com/tutorials/is-active"https://webdesign.tutsplus.com/tutorials/);
7
button.setAttribute("https://webdesign.tutsplus.com/tutorials/data-state"https://webdesign.tutsplus.com/tutorials/, "https://webdesign.tutsplus.com/tutorials/active"https://webdesign.tutsplus.com/tutorials/);
8
} else {
9
button.classList.remove("https://webdesign.tutsplus.com/tutorials/is-active"https://webdesign.tutsplus.com/tutorials/);
10
button.setAttribute("https://webdesign.tutsplus.com/tutorials/data-state"https://webdesign.tutsplus.com/tutorials/, "https://webdesign.tutsplus.com/tutorials/inactive"https://webdesign.tutsplus.com/tutorials/)
11
}
12
}
Também queremos garantir que apenas um botão seja selecionado por vez e, se nenhum botão for selecionado, as postagens devem exibir os dados padrão para que possamos criar funções para lidar com essa lógica:
1
const resetFilterButtons = (currentButton) => {
2
const filterButtons = document.querySelectorAll("https://webdesign.tutsplus.com/tutorials/.filter-button"https://webdesign.tutsplus.com/tutorials/);
3
[...filterButtons].map(button => {
4
if (button != currentButton) {
5
button.classList.remove("https://webdesign.tutsplus.com/tutorials/is-active"https://webdesign.tutsplus.com/tutorials/);
6
button.setAttribute("https://webdesign.tutsplus.com/tutorials/data-state"https://webdesign.tutsplus.com/tutorials/, "https://webdesign.tutsplus.com/tutorials/inactive"https://webdesign.tutsplus.com/tutorials/)
7
}
8
})
9
}
10
11
const resetPosts = () => {
12
postsContainer.innerHTML = ""https://webdesign.tutsplus.com/tutorials/;
13
postsData.map((post) => createPost(post));
14
}
Por fim, podemos atualizar nossa função de clique do botão:
1
const handleButtonClick = (e, param) => {
2
const button = e.target;
3
const buttonState = button.getAttribute("https://webdesign.tutsplus.com/tutorials/data-state"https://webdesign.tutsplus.com/tutorials/);
4
resetFilterButtons(button);
5
6
if (buttonState =="https://webdesign.tutsplus.com/tutorials/inactive"https://webdesign.tutsplus.com/tutorials/) {
7
button.classList.add("https://webdesign.tutsplus.com/tutorials/is-active"https://webdesign.tutsplus.com/tutorials/);
8
button.setAttribute("https://webdesign.tutsplus.com/tutorials/data-state"https://webdesign.tutsplus.com/tutorials/, "https://webdesign.tutsplus.com/tutorials/active"https://webdesign.tutsplus.com/tutorials/);
9
handleFilterPosts(param)
10
} else {
11
button.classList.remove("https://webdesign.tutsplus.com/tutorials/is-active"https://webdesign.tutsplus.com/tutorials/);
12
button.setAttribute("https://webdesign.tutsplus.com/tutorials/data-state"https://webdesign.tutsplus.com/tutorials/, "https://webdesign.tutsplus.com/tutorials/inactive"https://webdesign.tutsplus.com/tutorials/)
13
resetPosts()
14
}
15
}
Conclusão
E com isso, construímos completamente um componente de filtragem de dados em uma página da Web usando JavaScript vanilla!