<header>
<div class="container header-content">
<img alt="envato-tuts+" class="logo" src="https://static.tutsplus.com/packs/media/images/tuts_logo-ae9e557ae6c08db82f47e6047900e39b.svg">
<div class="search-bar">
<input id="search" type="search" placeholder="🔍 Start typing to search..." list="search-suggestions" autocomplete="off"/>
<datalist id="search-suggestions">
<option value="JavaScript">
<option value="CSS">
<option value="Accessibility">
<option value="Web Design">
</datalist>
</div>
</div>
</header>
<main class="container">
<div class="profile__posts">
<div class="search-display"></div>
<div class="card-header">
<div class="card-header__title">Tutorials</div>
</div>
<div class="posts-container">
</div>
</div>
</main>
* {
box-sizing: border-box;
}
body {
font: normal 16px/26px system-ui, Roboto, Arial, sans-serif;
background: #fefefe;
color: #2a3744;
margin: 0;
padding: 0;
text-rendering: optimizeLegibility;
font-smoothing: antialiased;
text-size-adjust: none;
overflow-x: hidden;
}
header {
background: #282828;
position: relative;
font-family: system-ui, Roboto, sans-serif;
min-height: 75px;
display: flex;
align-items: center;
position: sticky;
top: -54px;
z-index: 2;
}
.logo {
height: 24px;
}
a {
color: #0085b6;
text-decoration: none;
border: none;
}
.container {
max-width: 1440px;
margin: 0 auto;
padding: 24px 24px 0px;
width: 100%;
}
.search-bar {
display: flex;
justify-content: center;
padding: 24px;
}
.search-bar input {
width: 50%;
min-width: 300px;
padding: 12px 24px;
border-radius: 24px;
font-size: 16px;
border: 0px;
outline: none;
}
.search-bar [list]::list-button,
.search-bar [list]::calendar-picker-indicator {
display: none !important;
}
.search-display {
text-align: center;
}
.card-header__title {
font-weight: 700;
font-size: 14px;
color: #8fa6b3;
}
.posts-container {
display: flex;
flex-wrap: wrap;
gap: 30px;
padding: 12px 0;
}
.post {
position: relative;
min-width: 300px;
flex: 0 0 25%;
border: 1px solid #e1e8ed;
border-radius: 4px;
min-height: 300px;
height: auto;
}
.post-title {
font: 700 18px/1.4em system-ui, Roboto, Arial, sans-serif;
color: #4a4a4a;
margin-top: 0;
display: box;
line-clamp: 3;
box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.post-image {
width: 100%;
height: 100%;
object-fit: cover;
object-position: top;
}
.post-content {
padding: 16px;
}
.post-preview {
overflow: hidden;
width: 100%;
text-align: center;
display: block;
margin: 0 auto;
border-bottom: 1px solid #e1e8ed;
line-height: 0;
height: 200px;
}
.post-tag {
display: inline-block;
margin-right: 10px;
margin-bottom: 10px;
padding: 0 8px;
color: #717171;
border: 1px solid #9b9b9b;
border-radius: 25px;
white-space: nowrap;
}
.filter-container {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
gap: 10px;
padding: 32px 0;
border-top: 1px solid #e4e4e4;
border-bottom: 1px solid #e4e4e4;
margin-bottom: 32px;
}
.filter-button {
transition: background-color 200ms, color 200ms;
background-color: transparent;
font: inherit;
cursor: pointer;
display: inline-block;
padding: 0 8px;
color: #717171;
border: 1px solid #9b9b9b;
border-radius: 25px;
font-size: 14px;
white-space: nowrap;
}
.filter-button:hover {
background-color: #f3f3f3;
color: #3a3a3a;
}
.filter-button.is-active {
background-color: #0085b6;
border-color: #0085b6;
color: #fff;
}
let postsData = "";
const postsContainer = document.querySelector(".posts-container");
const searchDisplay = document.querySelector(".search-display");
fetch(
"https://gist.githubusercontent.com/jemimaabu/564beec0a30dbd7d63a90a153d2bc80b/raw/0b7e25ba0ebee6dbba216cfcfbae72d460a60f26/tutorial-levels"
).then(async (response) => {
postsData = await response.json();
postsData.map((post) => createPost(post));
});
const createPost = (postData) => {
const { title, link, image, categories } = postData;
const post = document.createElement("div");
post.className = "post";
post.innerHTML = `
<a class="post-preview" href="${link}" target="_blank">
<img class="post-image" src="${image}">
</a>
<div class="post-content">
<p class="post-title">${title}</p>
<div class="post-tags">
${categories
.map((category) => {
return '<span class="post-tag">' + category + "</span>";
})
.join("")}
</div>
</div>
`;
postsContainer.append(post);
};
const handleSearchPosts = (query) => {
const searchQuery = query.trim().toLowerCase();
if (searchQuery.length <= 1) {
resetPosts()
return
}
let searchResults = [postsData].filter(
(post) =>
post.categories.some((category) => category.toLowerCase().includes(searchQuery)) ||
post.title.toLowerCase().includes(searchQuery)
);
if (searchResults.length == 0) {
searchDisplay.innerHTML = "No results found"
} else if (searchResults.length == 1) {
searchDisplay.innerHTML = `1 result found for your query: ${query}`
} else {
searchDisplay.innerHTML = `${searchResults.length} results found for your query: ${query}`
}
postsContainer.innerHTML = "";
searchResults.map((post) => createPost(post));
};
const resetPosts = () => {
searchDisplay.innerHTML = ""
postsContainer.innerHTML = "";
postsData.map((post) => createPost(post));
};
const search = document.getElementById("search");
let debounceTimer;
const debounce = (callback, time) => {
window.clearTimeout(debounceTimer);
debounceTimer = window.setTimeout(callback, time);
};
search.addEventListener(
"input",
(event) => {
const query = event.target.value;
debounce(() => handleSearchPosts(query), 500);
},
false
);
This Pen doesn't use any external JavaScript resources.