1const wrapper = document.querySelector('.blogs-and-filter-wrapper') 2const postsWrapper = wrapper.querySelector('.blogs-wrapper') 3const postData = JSON.parse(wrapper.querySelector('#post-data').textContent) 4const filters = [...wrapper.querySelectorAll('.category-filter')] 5const selectAllBox = document.querySelector('.select-all') 6const dropdown = document.querySelector('.dropdown') 7const allCheckboxes = [selectAllBox, ...filters] 8const filterMenuToggle = document.querySelector('.dropdown-toggle') 9 10// create post links 11const createAnchor = (postData) => { 12 const anchor = document.createElement('a') 13 anchor.href = postData.url 14 anchor.classList = 'post-link' 15 anchor.innerHTML = ` 16 <h3 class="title-4">${postData.title}</h3> 17 <time pubdate datetime="${postData.date}" class="blog-date body">${postData.date}</time> 18 <p class="body">${postData.excerpt}</p> 19 ${postData.categories.reduce((markup, category) => { 20 return markup + ` <span class="category body">${category}</span>` 21 }, '')} 22 ` 23 return anchor 24} 25 26// checks all filters 27const selectAllCategories = (selectAllBox, checkboxes) => { 28 if (selectAllBox.checked) { 29 checkboxes.forEach((checkbox) => { 30 checkbox.checked = true 31 }) 32 33 updatePosts(filterPosts(elementsCache, checkboxes), postsWrapper) 34 } 35} 36 37const filterPosts = (elementsCache, checkboxes) => { 38 // If all categories are selected, bypass filter check 39 if (selectAllBox.checked) return elementsCache 40 41 const activeFilters = checkboxes 42 .filter((checkbox) => checkbox.checked) 43 .map((checkbox) => checkbox.value) 44 const filteredPosts = elementsCache.filter((post) => { 45 return post.data.categories.some((category) => { 46 return activeFilters.includes(category) 47 }) 48 }) 49 50 return filteredPosts 51} 52 53const updatePosts = (posts, postsWrapper) => { 54 postsWrapper.innerHTML = '' 55 for (let post of posts) { 56 postsWrapper.appendChild(post.elemenet) 57 } 58} 59 60const elementsCache = postData.map((post) => { 61 return { 62 data: post, 63 elemenet: createAnchor(post), 64 } 65}) 66 67updatePosts(filterPosts(elementsCache, filters), postsWrapper, postsWrapper) 68 69filterMenuToggle.addEventListener('click', () => { 70 dropdown.classList.toggle('active') 71 72 const isExpanded = filterMenuToggle.getAttribute('aria-expanded') === 'true' 73 filterMenuToggle.toggleAttribute('aria-expanded', !isExpanded) 74}) 75 76// Close the dropdown if clicked outside 77window.addEventListener('click', (evt) => { 78 if (!dropdown.contains(evt.target)) { 79 dropdown.classList.remove('active') 80 filterMenuToggle.setAttribute('aria-expanded', 'false') 81 } 82}) 83 84// Select all category filters 85selectAllBox.addEventListener('click', () => 86 selectAllCategories(selectAllBox, filters), 87) 88 89filters.forEach((checkbox) => { 90 checkbox.addEventListener('change', () => { 91 const enabledFilters = filters.filter((checkbox) => checkbox.checked) 92 93 if (enabledFilters.length === 1) { 94 enabledFilters[0].disabled = true 95 } else { 96 filters.forEach((checkbox) => (checkbox.disabled = false)) 97 } 98 99 // If every box is checked, select all 100 if (enabledFilters.length === filters.length) { 101 selectAllBox.checked = true 102 selectAllBox.disabled = true 103 // Uncheck all select all if filter was unchecked 104 } else if (!checkbox.checked && selectAllBox.checked) { 105 selectAllBox.checked = false 106 selectAllBox.disabled = false 107 } 108 109 updatePosts(filterPosts(elementsCache, filters), postsWrapper) 110 }) 111})
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.