6
\$\begingroup\$

I have an image gallery of old internet banner ads that pulls URLs and metadata from a JSON file and then displays them based on a set of user-selectable filters. The filters can show/exclude images based on metadata stored in the JSON file. Any dynamic stuff needs to be client-side (my webhost is a static host, and I'd like to keep it that way), which is why I'm not using a backend.

Some of the options in the filter are generated dynamically depending on what metadata exists in the JSON file (such as the image resolutions). A function identifies images that will be below the fold and adds loading="lazy" to them. Each filter label is also appended with the number of images that will display when the filter is selected. I've added each option for the advertiser filter to the HTML manually because there's certain ways I'd like to style the text for some of the company names.

This is the first time I've handled a Javascript project this big myself, and I was looking for ways to improve my code. The CSS is just some really simple styling. I haven't really thought through any other ways I could implement this. I'm most interested in how to improve the usability/accessibility/performance of my implementation. I'm also interested in the readability of my code, since I've been the only eyes on this so far.

Each JSON entry looks something like this (generated automatically with a Python script, full here):

 { "filepath": "ad-archive/economist/ft-4.gif", "source": "economist", "resolution": { "width": 120, "height": 800 }, "subjects": [ "news", "finance" ], "advertiser": "ft", "alt": "A skyscraper ad for the Financial Times advertising its 4 week risk-free trial.'", "colors": ["beige", "gray"], "keywords": [], "bytes": 64068, "adult": false, "url": "https://ads.sen.fish/ad-archive/economist/ft-4.gif" }, 

The code looks like this:

let imagesData = []; function getActiveFilters() { const groups = ['source', 'size', 'advertiser', 'subject', 'file-type']; const filters = {}; groups.forEach(group => { filters[group] = Array.from(document.querySelectorAll(`#${group} input[type="checkbox"]:checked`)) .map(chk => chk.value); }); return filters; } function matchesCandidate(entry, groupName, candidate) { switch (groupName) { case "source": return entry.source === candidate; case "size": return `${entry.resolution.width}x${entry.resolution.height}` === candidate; case "advertiser": return entry.advertiser === candidate; case "subject": return entry.subjects && entry.subjects.includes(candidate); case "file-type": return entry.filepath.split('.').pop().toLowerCase() === candidate; default: return false; } } function passesOtherFilters(entry, activeFilters, excludeGroup) { for (const group in activeFilters) { if (group === excludeGroup) continue; const values = activeFilters[group]; if (values.length > 0) { switch (group) { case "source": if (!values.includes(entry.source)) return false; break; case "size": const entrySize = `${entry.resolution.width}x${entry.resolution.height}`; if (!values.includes(entrySize)) return false; break; case "advertiser": if (!values.includes(entry.advertiser)) return false; break; case "subject": if (!values.some(val => entry.subjects.includes(val))) return false; break; case "file-type": const ext = entry.filepath.split('.').pop().toLowerCase(); if (!values.includes(ext)) return false; break; } } } return true; } function countForGroupCandidate(groupName, candidate, activeFilters) { let count = 0; imagesData.forEach(entry => { if (passesOtherFilters(entry, activeFilters, groupName)) { if (matchesCandidate(entry, groupName, candidate)) { count++; } } }); return count; } function populateSizeCheckboxes() { const sizes = new Set(); imagesData.forEach(entry => { const sizeStr = `${entry.resolution.width}x${entry.resolution.height}`; sizes.add(sizeStr); }); const sizesArray = Array.from(sizes).sort((a, b) => { const [wA, hA] = a.split('x').map(Number); const [wB, hB] = b.split('x').map(Number); return (wA * hA) - (wB * hB); }); const sizeContainer = document.getElementById("size"); sizeContainer.innerHTML = ""; sizesArray.forEach(sizeStr => { const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.name = "size"; checkbox.value = sizeStr; checkbox.id = "size-" + sizeStr; checkbox.addEventListener("change", updateGallery); const label = document.createElement("label"); label.htmlFor = checkbox.id; label.dataset.original = sizeStr; label.textContent = sizeStr; sizeContainer.appendChild(checkbox); sizeContainer.appendChild(label); }); } function updateFilterCounts() { const activeFilters = getActiveFilters(); const groups = ['source', 'size', 'advertiser', 'subject', 'file-type']; groups.forEach(groupId => { const container = document.getElementById(groupId); const checkboxes = container.querySelectorAll('input[type="checkbox"]'); checkboxes.forEach(chk => { const label = container.querySelector(`label[for="${chk.id}"]`); if (label) { if (!label.dataset.original) { label.dataset.original = label.textContent; } const count = countForGroupCandidate(groupId, chk.value, activeFilters); label.textContent = `${label.dataset.original} (${count})`; label.dataset.count = count; } }); if (['source', 'size', 'advertiser', 'file-type'].includes(groupId)) { const activeEl = document.activeElement; let activeId = null; if (activeEl && activeEl.tagName === 'INPUT' && activeEl.parentElement === container) { activeId = activeEl.id; } const pairs = Array.from(container.querySelectorAll('input[type="checkbox"]')).map(chk => { const label = container.querySelector(`label[for="${chk.id}"]`); const count = label ? parseInt(label.dataset.count, 10) || 0 : 0; return { input: chk, label: label, count: count, checked: chk.checked }; }); pairs.sort((a, b) => { if (a.checked && !b.checked) return -1; if (!a.checked && b.checked) return 1; return b.count - a.count; }); container.innerHTML = ""; pairs.forEach(pair => { container.appendChild(pair.input); container.appendChild(pair.label); }); if (activeId) { const newActive = container.querySelector(`#${activeId}`); if (newActive) { newActive.focus(); } } } }); } function updateGallery() { const activeFilters = getActiveFilters(); let filtered = imagesData.filter(entry => { let match = true; if (activeFilters.source.length > 0) { match = match && activeFilters.source.includes(entry.source); } if (activeFilters.size.length > 0) { const entrySize = `${entry.resolution.width}x${entry.resolution.height}`; match = match && activeFilters.size.includes(entrySize); } if (activeFilters.advertiser.length > 0) { match = match && activeFilters.advertiser.includes(entry.advertiser); } if (activeFilters.subject.length > 0) { match = match && activeFilters.subject.some(val => entry.subjects.includes(val)); } if (activeFilters["file-type"].length > 0) { const ext = entry.filepath.split('.').pop().toLowerCase(); match = match && activeFilters["file-type"].includes(ext); } return match; }); filtered.sort((a, b) => (a.resolution.width * a.resolution.height) - (b.resolution.width * b.resolution.height) ); const gallery = document.getElementById("gallery"); gallery.innerHTML = ""; filtered.forEach(entry => { const gridItem = document.createElement("div"); gridItem.classList.add("gallery-item"); const anchor = document.createElement("a"); anchor.href = entry.url; anchor.setAttribute("download", ""); const img = document.createElement("img"); img.src = entry.url; img.width = entry.resolution.width; img.height = entry.resolution.height; img.alt = entry.alt || ""; img.loading = "lazy"; anchor.appendChild(img); gridItem.appendChild(anchor); gallery.appendChild(gridItem); }); document.getElementById("gallery-count").textContent = `${filtered.length} results (${imagesData.length - filtered.length} ads filtered)`; updateLazyImages(); updateFilterCounts(); } function updateLazyImages() { const imgs = document.querySelectorAll("#gallery img"); imgs.forEach(img => { const rect = img.getBoundingClientRect(); if (rect.top < window.innerHeight) { img.removeAttribute("loading"); } }); } function resetFilters() { document.querySelectorAll(".filters input[type='checkbox']").forEach(chk => { chk.checked = false; }); updateGallery(); } function shareSearch() { const groups = ['source', 'size', 'advertiser', 'subject', 'file-type']; const params = new URLSearchParams(); groups.forEach(groupId => { const values = Array.from(document.querySelectorAll(`#${groupId} input[type="checkbox"]:checked`)) .map(chk => chk.value); if (values.length > 0) { params.set(groupId, values.join(',')); } }); const shareUrl = window.location.origin + window.location.pathname + '?' + params.toString(); navigator.clipboard.writeText(shareUrl) .then(() => alert("Search URL copied to clipboard:\n" + shareUrl)) .catch(err => alert("Failed to copy URL: " + err)); } function toggleSizesByAspect(aspectType) { const checkboxes = Array.from(document.querySelectorAll("#size input[type='checkbox']")) .filter(chk => { const [w, h] = chk.value.split('x').map(Number); const ratio = w / h; if (aspectType === "horizontal") return ratio >= 2; if (aspectType === "vertical") return ratio <= 0.5; if (aspectType === "square") return ratio > 0.5 && ratio < 2; return false; }); const allSelected = checkboxes.every(chk => chk.checked); checkboxes.forEach(chk => { chk.checked = !allSelected; }); updateGallery(); } function applyQueryParameters() { const params = new URLSearchParams(window.location.search); ['source', 'size', 'advertiser', 'subject', 'file-type'].forEach(groupId => { if (params.has(groupId)) { const values = params.get(groupId).split(','); const checkboxes = document.querySelectorAll(`#${groupId} input[type="checkbox"]`); checkboxes.forEach(chk => { if (values.includes(chk.value)) { chk.checked = true; } }); } }); } document.querySelectorAll(".filters input[type='checkbox']").forEach(chk => { chk.addEventListener("change", updateGallery); }); document.getElementById("reset-btn").addEventListener("click", resetFilters); document.getElementById("share-btn").addEventListener("click", shareSearch); document.getElementById("horizontal-btn").addEventListener("click", () => toggleSizesByAspect("horizontal")); document.getElementById("square-btn").addEventListener("click", () => toggleSizesByAspect("square")); document.getElementById("vertical-btn").addEventListener("click", () => toggleSizesByAspect("vertical")); fetch("https://ads.sen.fish/image_metadata.json") .then(response => response.json()) .then(data => { imagesData = data; document.getElementById("count").innerText = imagesData.length; populateSizeCheckboxes(); applyQueryParameters(); updateGallery(); }) .catch(error => console.error("Error loading JSON:", error));
:root { --link-color: #1111ee; } * { box-sizing: border-box; padding: 0; margin: 0; } button { background: none; border: none; font-family: 'Times New Roman', Times, serif; font-size: 1rem; color: var(--link-color); text-decoration: underline; cursor: pointer; } legend { font-weight: bold; margin-inline-start: 3px; } button:hover { text-decoration: none; } .noscript { display: block; font-size: 1.1rem; text-align: center; margin: 1rem 0.5rem; } .filter-wrapper { background-color: rgb(235, 235, 235); border-top: 2px solid black; border-bottom: 2px solid black; padding-inline: 0.5rem; z-index: 1000; } .filters { max-width: 1200px; margin: 0 auto; } details { width: 100%; } summary { padding: 1rem 0; } summary:hover { cursor: pointer; } summary * { display: inline; } summary, summary * { font-size: 1.1rem; } .filters fieldset { background-color: white; max-height: 8rem; overflow-y: scroll; } .tag-style input { height: 0; width: 0; margin: 0; padding: 0; } .tag-style input:focus-within { outline: none; } .tag-style input:focus-visible+label { outline: 2px solid var(--link-color); } .tag-style label { background-color: #EEE; border: 1px solid black; cursor: pointer; padding: 0.35rem; display: inline-block; margin: 2px; user-select: none; } .tag-style input[type='checkbox']:checked+label { background-color: #444; color: white; } .filters h1, .filters h3 { font-size: 1.05rem; } #gallery { padding-top: 16px; display: flex; flex-wrap: wrap; justify-content: center; align-items: center; padding-bottom: 100px; } .gallery-item { transition: transform 0.4s; border: 2px solid transparent; } .gallery-item a { display: inline-block; } @media (hover: hover) { body:has(.gallery-item:hover) .gallery-item:hover { border: 2px solid lime; filter: none; transform: scale(1.2); z-index: 5; } .tag-style label:hover { background-color: #CCCCE0; } .tag-style input[type='checkbox']:checked+label:hover { background-color: #444466; } } .gallery-item img { max-width: 100%; height: auto; vertical-align: middle; background-color: rgba(128, 128, 128, 0.311); font-style: italic; color: black; } main { background-color: white; min-height: 100vh; }
<div class="filter-wrapper"> <form class="filters"> <details> <summary> <h2>Limit your search</h2> <span id="gallery-count"></span> </summary> <fieldset> <legend id="source-label">Source</legend> <div id="source" class="tag-style"> <input type="checkbox" name="source" value="cnn" id="source-cnn"> <label for="source-cnn">CNN</label> <input type="checkbox" name="source" value="economist" id="source-economist"> <label for="source-economist">The Economist</label> <input type="checkbox" name="source" value="financial-times" id="source-financial-times"> <label for="source-financial-times">Financial Times</label> <input type="checkbox" name="source" value="wired" id="source-wired"> <label for="source-wired">Wired</label> </div> </fieldset> <fieldset> <legend id="size-label">Image size</legend> <button type="button" id="horizontal-btn">Select horizontal ads</button> <button type="button" id="square-btn">Select square ads</button> <button type="button" id="vertical-btn">Select vertical ads</button> <div id="size" class="tag-style"> <!-- Size checkboxes will be generated dynamically --> </div> </fieldset> <fieldset> <legend id="advertiser-label">Advertiser</legend> <div id="advertiser" class="tag-style"> <!-- 1 --> <input type="checkbox" name="advertiser" value="1-800-flowers" id="a-1-800-flowers"> <label for="a-1-800-flowers">1-800-Flowers</label> <input type="checkbox" name="advertiser" value="3m" id="a-3m"> <label for="a-3m">3M</label> <!-- a --> <input type="checkbox" name="advertiser" value="aaa" id="a-aaa"> <label for="a-aaa">AAA</label> <input type="checkbox" name="advertiser" value="about.com" id="a-about.com"> <label for="a-about.com">About.com</label> <input type="checkbox" name="advertiser" value="aci-worldwide" id="a-aci-worldwide"> <label for="a-aci-worldwide">ACI Worldwide</label> <input type="checkbox" name="advertiser" value="agilent" id="a-agilent"> <label for="a-agilent">Agilent Technologies</label> <input type="checkbox" name="advertiser" value="air-france" id="a-air-france"> <label for="a-air-france">Air France</label> <input type="checkbox" name="advertiser" value="american-college-of-gastroenterology" id="a-american-college-of-gastroenterology"> <label for="a-american-college-of-gastroenterology">American College of Gastroenterology</label> <input type="checkbox" name="advertiser" value="ameritrade" id="a-ameritrade"> <label for="a-ameritrade">Ameritrade</label> <input type="checkbox" name="advertiser" value="anna-and-the-king" id="a-anna-and-the-king"> <label for="a-anna-and-the-king">Anna and the King</label> <input type="checkbox" name="advertiser" value="aol" id="a-aol"> <label for="a-aol">AOL</label> <input type="checkbox" name="advertiser" value="apartments.com" id="a-apartments.com"> <label for="a-apartments.com">Apartments.com</label> <input type="checkbox" name="advertiser" value="archer-daniels-midland" id="a-archer-daniels-midland"> <label for="a-archer-daniels-midland">Archer Daniels Midland</label> <input type="checkbox" name="advertiser" value="arthur-andersen" id="a-arthur-andersen"> <label for="a-arthur-andersen">Arthur Andersen</label> <input type="checkbox" name="advertiser" value="autoconnect" id="a-autoconnect"> <label for="a-autoconnect">AutoConnect</label> <input type="checkbox" name="advertiser" value="aventis" id="a-aventis"> <label for="a-aventis">Aventis</label> <!-- b --> <input type="checkbox" name="advertiser" value="bahamas.com" id="a-bahamas.com"> <label for="a-bahamas.com">Bahamas.com</label> <input type="checkbox" name="advertiser" value="barnes-and-noble" id="a-barnes-and-noble"> <label for="a-barnes-and-noble">Barnes and Noble</label> <input type="checkbox" name="advertiser" value="befree" id="a-befree"> <label for="a-befree">BeFree</label> <input type="checkbox" name="advertiser" value="bellsouth" id="a-bellsouth"> <label for="a-bellsouth">BellSouth</label> <input type="checkbox" name="advertiser" value="bigstar" id="a-bigstar"> <label for="a-bigstar">BigStar</label> <input type="checkbox" name="advertiser" value="black-lab-micro" id="a-black-lab-micro"> <label for="a-black-lab-micro">Black Lab micro</label> <input type="checkbox" name="advertiser" value="blanchard" id="a-blanchard"> <label for="a-blanchard">Blanchard and Company</label> <input type="checkbox" name="advertiser" value="bmw" id="a-bmw"> <label for="a-bmw">BMW</label> <input type="checkbox" name="advertiser" value="bnp-paribas" id="a-bnp-paribas"> <label for="a-bnp-paribas">BNP Paribas</label> <input type="checkbox" name="advertiser" value="brand-institute" id="a-brand-institute"> <label for="a-brand-institute">Brand Institute</label> <input type="checkbox" name="advertiser" value="brandsforless.com" id="a-brandsforless.com"> <label for="a-brandsforless.com">BrandsForLess.com</label> <input type="checkbox" name="advertiser" value="bugle-boy" id="a-bugle-boy"> <label for="a-bugle-boy">Bugle Boy</label> <input type="checkbox" name="advertiser" value="business-marketplace" id="a-business-marketplace"> <label for="a-business-marketplace">Business Marketplace</label> <input type="checkbox" name="advertiser" value="busy-body" id="a-busy-body"> <label for="a-busy-body">Busy Body</label> <input type="checkbox" name="advertiser" value="buyandhold.com" id="a-buyandhold.com"> <label for="a-buyandhold.com">BuyandHold.com</label> <input type="checkbox" name="advertiser" value="buycomp.com" id="a-buycomp.com"> <label for="a-buycomp.com">Buycomp.com</label> <!-- c --> <input type="checkbox" name="advertiser" value="capgemini" id="a-capgemini"> <label for="a-capgemini">Capgemini</label> <input type="checkbox" name="advertiser" value="careerbuilder" id="a-careerbuilder"> <label for="a-careerbuilder">CareerBuilder</label> <input type="checkbox" name="advertiser" value="carparts.com" id="a-carparts.com"> <label for="a-carparts.com">CarParts.com</label> <input type="checkbox" name="advertiser" value="cars.com" id="a-cars.com"> <label for="a-cars.com">Cars.com</label> <input type="checkbox" name="advertiser" value="cartoon-network" id="a-cartoon-network"> <label for="a-cartoon-network">Cartoon Network</label> <input type="checkbox" name="advertiser" value="catholic-relief-services" id="a-catholic-relief-services"> <label for="a-catholic-relief-services">Catholic Relief Services</label> <input type="checkbox" name="advertiser" value="cbs" id="a-cbs"> <label for="a-cbs">CBS</label> <input type="checkbox" name="advertiser" value="cdnow" id="a-cdnow"> <label for="a-cdnow">CDNow</label> <input type="checkbox" name="advertiser" value="cdw" id="a-cdw"> <label for="a-cdw">CDW</label> <input type="checkbox" name="advertiser" value="charitywave.com" id="a-charitywave.com"> <label for="a-charitywave.com">CharityWave.com</label> <input type="checkbox" name="advertiser" value="charles-schwab" id="a-charles-schwab"> <label for="a-charles-schwab">Charles Schwab</label> <input type="checkbox" name="advertiser" value="cigna" id="a-cigna"> <label for="a-cigna">Cigna</label> <input type="checkbox" name="advertiser" value="cnet" id="a-cnet"> <label for="a-cnet">CNET</label> <input type="checkbox" name="advertiser" value="cnn" id="a-cnn"> <label for="a-cnn">CNN</label> <input type="checkbox" name="advertiser" value="compaq" id="a-compaq"> <label for="a-compaq">Compaq</label> <input type="checkbox" name="advertiser" value="computer-associates" id="a-computer-associates"> <label for="a-computer-associates">Computer Associates</label> <input type="checkbox" name="advertiser" value="consolidated-mortgage-and-financial-services" id="a-consolidated-mortgage-and-financial-services"> <label for="a-consolidated-mortgage-and-financial-services">Consolidated Mortgage & Financial Services</label> <input type="checkbox" name="advertiser" value="contactsingapore.org.sg" id="a-contactsingapore.org.sg"> <label for="a-contactsingapore.org.sg">ContactSingapore.org.sg</label> <input type="checkbox" name="advertiser" value="crossworlds" id="a-crossworlds"> <label for="a-crossworlds">Crossworlds</label> <!-- d --> <input type="checkbox" name="advertiser" value="datamation" id="a-datamation"> <label for="a-datamation">Datamation</label> <input type="checkbox" name="advertiser" value="dell" id="a-dell"> <label for="a-dell">Dell</label> <input type="checkbox" name="advertiser" value="delta" id="a-delta"> <label for="a-delta">Delta</label> <input type="checkbox" name="advertiser" value="derwent" id="a-derwent"> <label for="a-derwent">Derwent Information</label> <input type="checkbox" name="advertiser" value="developer.com" id="a-developer.com"> <label for="a-developer.com">Developer.com</label> <input type="checkbox" name="advertiser" value="diamondcluster" id="a-diamondcluster"> <label for="a-diamondcluster">DiamondCluster</label> <input type="checkbox" name="advertiser" value="digital-insight" id="a-digital-insight"> <label for="a-digital-insight">Digital Insight</label> <input type="checkbox" name="advertiser" value="ditech.com" id="a-ditech.com"> <label for="a-ditech.com">ditech.com</label> <input type="checkbox" name="advertiser" value="dreyfus" id="a-dreyfus"> <label for="a-dreyfus">Dreyfus Corporation</label> <input type="checkbox" name="advertiser" value="drugemporium.com" id="a-drugemporium.com"> <label for="a-drugemporium.com">DrugEmporium.com</label> <!-- e --> <input type="checkbox" name="advertiser" value="e-business-world" id="a-e-business-world"> <label for="a-e-business-world">e-Business World</label> <input type="checkbox" name="advertiser" value="e-centives" id="a-e-centives"> <label for="a-e-centives">e-centives</label> <input type="checkbox" name="advertiser" value="ebay" id="a-ebay"> <label for="a-ebay">eBay</label> <input type="checkbox" name="advertiser" value="economic-research-institute" id="a-economic-research-institute"> <label for="a-economic-research-institute">Economic Research Institute</label> <input type="checkbox" name="advertiser" value="economist" id="a-economist"> <label for="a-economist">The Economist</label> <input type="checkbox" name="advertiser" value="environmental-defense-fund" id="a-environmental-defense-fund"> <label for="a-environmental-defense-fund">Environmental Defense Fund</label> <input type="checkbox" name="advertiser" value="equant" id="a-equant"> <label for="a-equant">Equant</label> <input type="checkbox" name="advertiser" value="equilibrium" id="a-equilibrium"> <label for="a-equilibrium">Equilibrium</label> <input type="checkbox" name="advertiser" value="european-central-bank" id="a-european-central-bank"> <label for="a-european-central-bank">European Central Bank</label> <input type="checkbox" name="advertiser" value="ein" id="a-ein"> <label for="a-ein">European Internet Network</label> <input type="checkbox" name="advertiser" value="european-jewellery" id="a-european-jewellery"> <label for="a-european-jewellery">European Jewellery</label> <input type="checkbox" name="advertiser" value="extensis" id="a-extensis"> <label for="a-extensis">Extensis</label> <!-- f --> <input type="checkbox" name="advertiser" value="feed" id="a-feed"> <label for="a-feed">Feed</label> <input type="checkbox" name="advertiser" value="ft" id="a-ft"> <label for="a-ft">Financial Times</label> <input type="checkbox" name="advertiser" value="ford" id="a-ford"> <label for="a-ford">Ford</label> <input type="checkbox" name="advertiser" value="fox" id="a-fox"> <label for="a-fox">Fox</label> <input type="checkbox" name="advertiser" value="france-telecom" id="a-france-telecom"> <label for="a-france-telecom">france telecom</label> <input type="checkbox" name="advertiser" value="frontier-economics.com" id="a-frontier-economics.com"> <label for="a-frontier-economics.com">frontier-economics.com</label> <!-- g --> <input type="checkbox" name="advertiser" value="gamedealer.com" id="a-gamedealer.com"> <label for="a-gamedealer.com">GameDealer.com</label> <input type="checkbox" name="advertiser" value="gavelnet.com" id="a-gavelnet.com"> <label for="a-gavelnet.com">Gavelnet.com</label> <input type="checkbox" name="advertiser" value="general-magic" id="a-general-magic"> <label for="a-general-magic">General Magic</label> <input type="checkbox" name="advertiser" value="getsmart.com" id="a-getsmart.com"> <label for="a-getsmart.com">GetSmart.com</label> <input type="checkbox" name="advertiser" value="giftworld.com" id="a-giftworld.com"> <label for="a-giftworld.com">Giftworld.com</label> <input type="checkbox" name="advertiser" value="globalnetfinancial" id="a-globalnetfinancial"> <label for="a-globalnetfinancial">GlobalNetFinancial</label> <input type="checkbox" name="advertiser" value="globes" id="a-globes"> <label for="a-globes">Globes</label> <input type="checkbox" name="advertiser" value="guinness-mahon" id="a-guinness-mahon"> <label for="a-guinness-mahon">Guinness Mahon</label> <!-- h --> <input type="checkbox" name="advertiser" value="hardware.com" id="a-hardware.com"> <label for="a-hardware.com">hardware.com</label> <input type="checkbox" name="advertiser" value="hob.com" id="a-hob.com"> <label for="a-hob.com">hob.com</label> <input type="checkbox" name="advertiser" value="homeportfolio.com" id="a-homeportfolio.com"> <label for="a-homeportfolio.com">homeportfolio.com</label> <input type="checkbox" name="advertiser" value="hotbot" id="a-hotbot"> <label for="a-hotbot">HotBot</label> <input type="checkbox" name="advertiser" value="hp" id="a-hp"> <label for="a-hp">HP</label> <input type="checkbox" name="advertiser" value="hummingbird" id="a-hummingbird"> <label for="a-hummingbird">Hummingbird</label> <input type="checkbox" name="advertiser" value="hyundai" id="a-hyundai"> <label for="a-hyundai">Hyundai</label> <!-- i --> <input type="checkbox" name="advertiser" value="ibm" id="a-ibm"> <label for="a-ibm">IBM</label> <input type="checkbox" name="advertiser" value="icelandair" id="a-icelandair"> <label for="a-icelandair">Icelandair</label> <input type="checkbox" name="advertiser" value="icollector.com" id="a-icollector.com"> <label for="a-icollector.com">icollector</label> <input type="checkbox" name="advertiser" value="idg.net" id="a-idg.net"> <label for="a-idg.net">IDG.net</label> <input type="checkbox" name="advertiser" value="improvenet" id="a-improvenet"> <label for="a-improvenet">ImproveNet</label> <input type="checkbox" name="advertiser" value="ipi" id="a-ipi"> <label for="a-ipi">In Phase International</label> <input type="checkbox" name="advertiser" value="india" id="a-india"> <label for="a-india">India</label> <input type="checkbox" name="advertiser" value="inktomi" id="a-inktomi"> <label for="a-inktomi">Inktomi</label> <input type="checkbox" name="advertiser" value="insidedhtml.com" id="a-insidedhtml.com"> <label for="a-insidedhtml.com">insideDHTML.com</label> <input type="checkbox" name="advertiser" value="intel" id="a-intel"> <label for="a-intel">Intel</label> <input type="checkbox" name="advertiser" value="international-reference-report" id="a-international-reference-report"> <label for="a-international-reference-report">International Reference Report</label> <input type="checkbox" name="advertiser" value="internetdiamonds.com" id="a-internetdiamonds.com"> <label for="a-internetdiamonds.com">InternetDiamonds.com</label> <input type="checkbox" name="advertiser" value="internethypergate.com" id="a-internethypergate.com"> <label for="a-internethypergate.com">internethypergate.com</label> <input type="checkbox" name="advertiser" value="intervu" id="a-intervu"> <label for="a-intervu">InterVU</label> <input type="checkbox" name="advertiser" value="intranet-journal" id="a-intranet-journal"> <label for="a-intranet-journal">Intranet Journal</label> <input type="checkbox" name="advertiser" value="invesco" id="a-invesco"> <label for="a-invesco">Invesco</label> <input type="checkbox" name="advertiser" value="invest-in-us" id="a-invest-in-us"> <label for="a-invest-in-us">Invest in US</label> <input type="checkbox" name="advertiser" value="icep" id="a-icep"> <label for="a-icep">Investments, Commerce and Tourism of Portugal</label> <input type="checkbox" name="advertiser" value="investors-business-daily" id="a-investors-business-daily"> <label for="a-investors-business-daily">Investor's Business Daily</label> <input type="checkbox" name="advertiser" value="iomega" id="a-iomega"> <label for="a-iomega">iomega</label> <input type="checkbox" name="advertiser" value="iown" id="a-iown"> <label for="a-iown">iOwn</label> <input type="checkbox" name="advertiser" value="iprint.com" id="a-iprint.com"> <label for="a-iprint.com">iPrint.com</label> <input type="checkbox" name="advertiser" value="isn" id="a-isn"> <label for="a-isn">ISN</label> <input type="checkbox" name="advertiser" value="isyndicate" id="a-isyndicate"> <label for="a-isyndicate">iSyndicate</label> <input type="checkbox" name="advertiser" value="itsbob.co.uk" id="a-itsbob.co.uk"> <label for="a-itsbob.co.uk">itsBob.co.uk</label> <!-- j --> <input type="checkbox" name="advertiser" value="john-hancock" id="a-john-hancock"> <label for="a-john-hancock">John Hancock</label> <input type="checkbox" name="advertiser" value="jpmorgan" id="a-jpmorgan"> <label for="a-jpmorgan">JPMorgan</label> <!-- k --> <input type="checkbox" name="advertiser" value="kodak" id="a-kodak"> <label for="a-kodak">Kodak</label> <input type="checkbox" name="advertiser" value="kpmg" id="a-kpmg"> <label for="a-kpmg">KPMG</label> <!-- l --> <input type="checkbox" name="advertiser" value="levenger.com" id="a-levenger.com"> <label for="a-levenger.com">Levenger.com</label> <input type="checkbox" name="advertiser" value="lincoln-financial-group" id="a-lincoln-financial-group"> <label for="a-lincoln-financial-group">Lincoln Financial Group</label> <!-- l --> <input type="checkbox" name="advertiser" value="lbs" id="a-lbs"> <label for="a-lbs">London Business School</label> <input type="checkbox" name="advertiser" value="lycos" id="a-lycos"> <label for="a-lycos">Lyocs</label> <!-- m --> <input type="checkbox" name="advertiser" value="match.com" id="a-match.com"> <label for="a-match.com">match.com</label> <input type="checkbox" name="advertiser" value="mayo-clinic" id="a-mayo-clinic"> <label for="a-mayo-clinic">Mayo Clinic</label> <input type="checkbox" name="advertiser" value="mci" id="a-mci"> <label for="a-mci">MCI</label> <input type="checkbox" name="advertiser" value="megaplay" id="a-megaplay"> <label for="a-megaplay">Megaplay</label> <input type="checkbox" name="advertiser" value="micrografx" id="a-micrografx"> <label for="a-micrografx">Micrografx</label> <input type="checkbox" name="advertiser" value="microsoft" id="a-microsoft"> <label for="a-microsoft">Microsoft</label> <input type="checkbox" name="advertiser" value="misc" id="a-misc"> <label for="a-misc">Miscellaneous</label> <input type="checkbox" name="advertiser" value="mitsubishi" id="a-mitsubishi"> <label for="a-mitsubishi">Mitsubishi</label> <input type="checkbox" name="advertiser" value="morley" id="a-morley"> <label for="a-morley">Morley Fund Management</label> <input type="checkbox" name="advertiser" value="moviestreet" id="a-moviestreet"> <label for="a-moviestreet">MovieStreet</label> <!-- n --> <input type="checkbox" name="advertiser" value="nec" id="a-nec"> <label for="a-nec">NEC</label> <input type="checkbox" name="advertiser" value="neoplanet" id="a-neoplanet"> <label for="a-neoplanet">NeoPlanet</label> <input type="checkbox" name="advertiser" value="newyorkjewelrydistrict.com" id="a-newyorkjewelrydistrict.com"> <label for="a-newyorkjewelrydistrict.com">newyorkjewelrydistrict.com</label> <input type="checkbox" name="advertiser" value="nikkei" id="a-nikkei"> <label for="a-nikkei">Nikkei</label> <input type="checkbox" name="advertiser" value="nikon" id="a-nikon"> <label for="a-nikon">Nikon</label> <input type="checkbox" name="advertiser" value="northwest-airlines" id="a-northwest-airlines"> <label for="a-northwest-airlines">Northwest Airlines</label> <input type="checkbox" name="advertiser" value="npr" id="a-npr"> <label for="a-npr">NPR</label> <input type="checkbox" name="advertiser" value="nuvomedia" id="a-nuvomedia"> <label for="a-nuvomedia">NuvoMedia</label> <input type="checkbox" name="advertiser" value="nyu" id="a-nyu"> <label for="a-nyu">NYU</label> <!-- o --> <input type="checkbox" name="advertiser" value="office-furniture-usa" id="a-office-furniture-usa"> <label for="a-office-furniture-usa">Office Furniture USA</label> <input type="checkbox" name="advertiser" value="onsale" id="a-onsale"> <label for="a-onsale">Onsale</label> <!-- p --> <input type="checkbox" name="advertiser" value="pacific-bell" id="a-pacific-bell"> <label for="a-pacific-bell">Pacific Bell</label> <input type="checkbox" name="advertiser" value="pagenet" id="a-pagenet"> <label for="a-pagenet">PagNet</label> <input type="checkbox" name="advertiser" value="paine-webber" id="a-paine-webber"> <label for="a-paine-webber">Paine Webber</label> <input type="checkbox" name="advertiser" value="philips" id="a-philips"> <label for="a-philips">Philips</label> <input type="checkbox" name="advertiser" value="placeware" id="a-placeware"> <label for="a-placeware">PlaceWare</label> <input type="checkbox" name="advertiser" value="plansoft.com" id="a-plansoft.com"> <label for="a-plansoft.com">PlanSoft</label> <input type="checkbox" name="advertiser" value="pwc" id="a-pwc"> <label for="a-pwc">PricewaterhouseCoopers</label> <input type="checkbox" name="advertiser" value="prima-sport" id="a-prima-sport"> <label for="a-prima-sport">Prima Sport</label> <!-- q --> <input type="checkbox" name="advertiser" value="qualcomm" id="a-qualcomm"> <label for="a-qualcomm">Qualcomm</label> <!-- r --> <input type="checkbox" name="advertiser" value="reachachild.org" id="a-reachachild.org"> <label for="a-reachachild.org">reachachild.org</label> <input type="checkbox" name="advertiser" value="red-cross" id="a-red-cross"> <label for="a-red-cross">Red Cross</label> <input type="checkbox" name="advertiser" value="rent.net" id="a-rent.net"> <label for="a-rent.net">Rent.net</label> <input type="checkbox" name="advertiser" value="ricola" id="a-ricola"> <label for="a-ricola">Ricola</label> <input type="checkbox" name="advertiser" value="rockwell-international" id="a-rockwell-international"> <label for="a-rockwell-international">Rockwell International</label> <!-- s --> <input type="checkbox" name="advertiser" value="salvation-army" id="a-salvation-army"> <label for="a-salvation-army">salvation-army</label> <input type="checkbox" name="advertiser" value="samsung" id="a-samsung"> <label for="a-samsung">Samsung</label> <input type="checkbox" name="advertiser" value="sap" id="a-sap"> <label for="a-sap">SAP</label> <input type="checkbox" name="advertiser" value="scotland-on-line" id="a-scotland-on-line"> <label for="a-scotland-on-line">Scotland On Line</label> <input type="checkbox" name="advertiser" value="scottrade" id="a-scottrade"> <label for="a-scottrade">Scottrade</label> <input type="checkbox" name="advertiser" value="sharepages.com" id="a-sharepages.com"> <label for="a-sharepages.com">Sharepages.com</label> <input type="checkbox" name="advertiser" value="silicon-graphics" id="a-silicon-graphics"> <label for="a-silicon-graphics">Silicon Graphics</label> <input type="checkbox" name="advertiser" value="silknet" id="a-silknet"> <label for="a-silknet">Silknet</label> <input type="checkbox" name="advertiser" value="sony" id="a-sony"> <label for="a-sony">Sony</label> <input type="checkbox" name="advertiser" value="spree.com" id="a-spree.com"> <label for="a-spree.com">Spree.com</label> <input type="checkbox" name="advertiser" value="springstreet" id="a-springstreet"> <label for="a-springstreet">SpringStreet</label> <input type="checkbox" name="advertiser" value="suck.com" id="a-suck.com"> <label for="a-suck.com">Suck.com</label> <input type="checkbox" name="advertiser" value="sun" id="a-sun"> <label for="a-sun">Sun Microsystems</label> <input type="checkbox" name="advertiser" value="suretrade.com" id="a-suretrade.com"> <label for="a-suretrade.com">Suretrade</label> <!-- t --> <input type="checkbox" name="advertiser" value="terra" id="a-terra"> <label for="a-terra">Terra</label> <input type="checkbox" name="advertiser" value="texas-instruments" id="a-texas-instruments"> <label for="a-texas-instruments">Texas Instruments</label> <input type="checkbox" name="advertiser" value="time" id="a-time"> <label for="a-time">Time magazine</label> <input type="checkbox" name="advertiser" value="tnt" id="a-tnt"> <label for="a-tnt">TNT</label> <input type="checkbox" name="advertiser" value="toshiba" id="a-toshiba"> <label for="a-toshiba">Toshiba</label> <input type="checkbox" name="advertiser" value="transpoint" id="a-transpoint"> <label for="a-transpoint">TransPoint</label> <input type="checkbox" name="advertiser" value="travelagentspecials.com" id="a-travelagentspecials.com"> <label for="a-travelagentspecials.com">TravelAgentSpecials.com</label> <input type="checkbox" name="advertiser" value="travelstore.com" id="a-travelstore.com"> <label for="a-travelstore.com">travelstore.com</label> <input type="checkbox" name="advertiser" value="trend-micro" id="a-trend-micro"> <label for="a-trend-micro">Trend Micro</label> <input type="checkbox" name="advertiser" value="trium" id="a-trium"> <label for="a-trium">TRIUM EMBA</label> <!-- u --> <input type="checkbox" name="advertiser" value="ubid" id="a-ubid"> <label for="a-ubid">uBid</label> <input type="checkbox" name="advertiser" value="unicef" id="a-unicef"> <label for="a-unicef">UNICEF</label> <input type="checkbox" name="advertiser" value="unisys" id="a-unisys"> <label for="a-unisys">Unisys</label> <input type="checkbox" name="advertiser" value="u-chicago" id="a-u-chicago"> <label for="a-u-chicago">University of Chicago</label> <input type="checkbox" name="advertiser" value="university-of-maryland-university-college" id="a-university-of-maryland-university-college"> <label for="a-university-of-maryland-university-college">University of Maryland University College</label> <input type="checkbox" name="advertiser" value="upmystreet.com" id="a-upmystreet.com"> <label for="a-upmystreet.com">UpMyStreet.com</label> <!-- v --> <input type="checkbox" name="advertiser" value="valeo" id="a-valeo"> <label for="a-valeo">Valeo</label> <input type="checkbox" name="advertiser" value="veuve-clicquot" id="a-veuve-clicquot"> <label for="a-veuve-clicquot">Veuve Clicquot</label> <input type="checkbox" name="advertiser" value="visa" id="a-visa"> <label for="a-visa">Visa</label> <input type="checkbox" name="advertiser" value="vnu" id="a-vnu"> <label for="a-vnu">VNU</label> <!-- w --> <input type="checkbox" name="advertiser" value="wavephore" id="a-wavephore"> <label for="a-wavephore">WavePhore</label> <input type="checkbox" name="advertiser" value="webmd" id="a-webmd"> <label for="a-webmd">WebMD</label> <input type="checkbox" name="advertiser" value="webmonkey" id="a-webmonkey"> <label for="a-webmonkey">Webmonkey</label> <input type="checkbox" name="advertiser" value="welsh-development-agency" id="a-welsh-development-agency"> <label for="a-welsh-development-agency">Welsh Development Agency</label> <input type="checkbox" name="advertiser" value="whitehead-mann" id="a-whitehead-mann"> <label for="a-whitehead-mann">Whitehead Mann</label> <input type="checkbox" name="advertiser" value="wide-learning" id="a-wide-learning"> <label for="a-wide-learning">Wide Learning</label> <input type="checkbox" name="advertiser" value="wingspanbank.com" id="a-wingspanbank.com"> <label for="a-wingspanbank.com">Wingspan Bank</label> <input type="checkbox" name="advertiser" value="women.com" id="a-women.com"> <label for="a-women.com">women.com</label> <input type="checkbox" name="advertiser" value="world-championship-wrestling" id="a-world-championship-wrestling"> <label for="a-world-championship-wrestling">World Championship Wrestling</label> <input type="checkbox" name="advertiser" value="world-economic-forum" id="a-world-economic-forum"> <label for="a-world-economic-forum">World Economic Forum</label> <input type="checkbox" name="advertiser" value="world-trade-center-relief-fund" id="a-world-trade-center-relief-fund"> <label for="a-world-trade-center-relief-fund">World Trade Center Relief Fund</label> <input type="checkbox" name="advertiser" value="worldroom.com" id="a-worldroom.com"> <label for="a-worldroom.com">Worldroom.com</label> <!-- x --> <!-- y --> <input type="checkbox" name="advertiser" value="yalplay-imvs" id="a-yalplay-imvs"> <label for="a-yalplay-imvs">yalplay</label> <input type="checkbox" name="advertiser" value="yamaha" id="a-yamaha"> <label for="a-yamaha">Yamaha</label> <!-- z --> <input type="checkbox" name="advertiser" value="ziff-davis" id="a-ziff-davis"> <label for="a-ziff-davis">Ziff Davis</label> </div> </fieldset> <fieldset> <legend id="subject-label">Category</legend> <div id="subject" class="tag-style"><input type="checkbox" name="subject" value="automotive" id="subject-automotive"><label for="subject-automotive">Automotive</label><input type="checkbox" name="subject" value="charity" id="subject-charity"><label for="subject-charity">Charities and Governments</label><input type="checkbox" name="subject" value="entertainment" id="subject-entertainment"><label for="subject-entertainment">Culture and Entertainment</label><input type="checkbox" name="subject" value="education" id="subject-education"><label for="subject-education">Education</label><input type="checkbox" name="subject" value="fashion" id="subject-fashion"><label for="subject-fashion">Fashion</label><input type="checkbox" name="subject" value="finance" id="subject-finance"><label for="subject-finance">Business and Finance</label><input type="checkbox" name="subject" value="health" id="subject-health"><label for="subject-health">Health</label><input type="checkbox" name="subject" value="home" id="subject-home"><label for="subject-home">Home</label><input type="checkbox" name="subject" value="news" id="subject-news"><label for="subject-news">News</label><input type="checkbox" name="subject" value="retail" id="subject-retail"><label for="subject-retail">Retail and Commerce</label><input type="checkbox" name="subject" value="sports" id="subject-sports"><label for="subject-sports">Sports</label><input type="checkbox" name="subject" value="technology" id="subject-technology"><label for="subject-technology">Technology</label><input type="checkbox" name="subject" value="travel" id="subject-travel"><label for="subject-travel">Travel</label></div> </fieldset> <fieldset> <legend id="file-type-label">File type</legend> <div id="file-type" class="tag-style"> <input type="checkbox" name="file-type" value="jpg" id="file-type-jpg"><label for="file-type-jpg">.jpg</label><input type="checkbox" name="file-type" value="gif" id="file-type-gif"><label for="file-type-gif">.gif</label> </div> </fieldset> <button type="button" id="share-btn">Share search</button> <button type="button" id="reset-btn">Reset filters</button> </details> </form> </div> <main> <div id='gallery'>For some reason, the code snippet here on StackExchange has some issues loading the json file. However, selecting any of the filters above should fix that issue.</div> <noscript> <span class="noscript">Sorry! You need <a href="https://www.enable-javascript.com/">enable javascript</a> to view this page. Alternatively, you can access the <a href="https://ads.sen.fish/image_metadata.json">json file</a> directly.</span> </noscript> </main>

For some reason, the code snippet on StackExchange has some issues loading the JSON file. However, selecting any of the filters fixes that issue. The original code for the project can be found here.

\$\endgroup\$

    1 Answer 1

    6
    +50
    \$\begingroup\$

    It's been a while since I looked too much at native JS (I use TS most of the time), so forgive me if any of my suggestions feel dated.

    CSS

    I have no real feedback on the CSS. I personally prefer using some kind of pre-processor on my CSS so I can use nesting and such, but it seems fine as-is.

    HTML

    The HTML generally seems fine. I do refuse to believe that your advertiser content has to be static like this, and you couldn't just use JSON to fetch data here as well. Frankly this seems true for your available sources, categories, and file types as well, but the advertisers are the most egregious.

    JavaScript

    Language Features

    First off, there are a couple just common language features/functions that you aren't using quite as well as you could be, that would tighten up your code in many places.

    Optional Chaining

    Optional chaining is well supported in modern browsers at this point. It can simplify things in a few places:

    return entry.subjects && entry.subjects.includes(candidate);

    becomes

    return entry.subjects?.includes(candidate);

    Array.from and mapFn

    The second argument to Array.from is a function that you can use to map values during the initial iteration, rather than having to do Array.from(iterable).map(mapFn). You can just do Array.from(iterable, mapFn) and make it more legible and probably slightly more performant.

    Places I updated to do this include:

    • getActiveFilters
    • populateSizeCheckboxes
    • updateFilterCounts
    • shareSearch

    Dropping curly braces

    This helps no-one. You save very few lines of code, and you're just setting yourself (or a future maintainer) up for a headache when you accidentally add a line of code that you thought would be conditionally executed but wasn't. Just add the curly braces instead.

    countForGroupCandidate

    This could just be replaced by Array.reduce:

    function countForGroupCandidate(groupName, candidate, activeFilters) { return imagesData.reduce( (count, entry) => count + (passesOtherFilters(entry, activeFilters, groupName) && matchesCandidate(entry, groupName, candidate)), 0 ); } 

    If you (rightfully so) dislike coercing booleans to integers for math purposes, you can easily just do count + ((bool stuff) ? 1 : 0).

    populateSizeCheckboxes

    You can construct a Set from an iterable:

    const sizes = new Set(imagesData.map(fmtResolution));

    Helper Functions

    There are a few small things that could pretty easily be wrapped up into helper functions that would benefit you

    Formatting sizes

    You have this code in a lot of places:

    `${entry.resolution.width}x${entry.resolution.height}` 

    Just wrap it in a function

    function fmtResolution(entry) { return `${entry.resolution.width}x${entry.resolution.height}`; } 

    You can have a similar de-formatting function if you'd like

    function deFmtResolution(sizeStr) { const [width, height] = sizeStr.split("x"); return { width: parseInt(width, 10), height: parseInt(height, 10) }; } 

    And even write an area function:

    function resolutionSize(sizeStr) { const { width, height } = defmtResolution(sizeStr); return width * height; } 

    You'll be able to use this a lot of other places.

    Getting the file extension

    In a few places you have code like this to get the file extension: entry.filepath.split('.').pop().toLowerCase()

    This unnecessarily creates and mutates an array. At first I was going to recommend using Array.at instead of pop so you don't have to actually modify the array - but you don't actually need an array at all. Just use String.lastIndexOf instead:

    entry.filepath.substring(entry.filepath.lastIndexOf(".") + 1).toLowerCase()

    This will eliminate creating the intermediate array, which probably will save you a touch of time and memory.

    function getEntryExtension(entry) { return entry.filepath.substring(entry.filepath.lastIndexOf(".") + 1).toLowerCase(); } 

    matchesCandidate

    After making the other changes above this isn't too bad. I personally preferred splitting off the "figure out what to compare" logic from the "do the comparison" logic, but this one feels more like personal preference:

    function matchesCandidate(entry, groupName, candidate) { const compareTo = matchesCandidateGetCompareTo(entry, groupName); if (compareTo) { if (typeof compareTo === "string") { return candidate === compareTo; } return compareTo.includes(candidate); } return false; } function matchesCandidateGetCompareTo(entry, groupName) { switch (groupName) { case "source": return entry.source; case "size": return fmtResolution(entry); case "advertiser": return entry.advertiser; case "subject": return entry.subjects; case "file-type": return getEntryExtension(entry); default: return undefined; } } 

    You could even take it a step further if you're into that:

    function matchesCandidate(entry, groupName, candidate) { const compareTo = matchesCandidateGetCompareTo(entry, groupName); return compareTo && typeof compareTo === "string" ? candidate === compareTo : compareTo.includes(candidate); } 

    passesOtherFilters

    You've effectively just re-implemented matchesCandidate inside of here. Re-use that function instead of implementing it again.

    function passesOtherFilters(entry, activeFilters, excludeGroup) { for (const group in activeFilters) { if (group === excludeGroup) { continue; } const values = activeFilters[group]; if (values.length > 0 && !values.some(value => matchesCandidate(entry, group, value))) { return false; } } return true; } 

    If you're so inclined, you could take this even further:

    // Assume I pulled this out of getActiveFilters and you now have a file constant with this array const groups = ['source', 'size', 'advertiser', 'subject', 'file-type']; function passesOtherFilters(entry, activeFilters, excludeGroup) { return !groups.some(groupName => groupName in activeFilters && groupName !== excludeGroup && activeFilters[groupName].length > 0 && !activeFilters[groupName].some(value => matchesCandidate(entry, groupName, value))); } 

    I will say that I'm a little skeptical of your implementation of this function, however. As written, it appears to return false as soon as it finds a filter that it doesn't pass, which appears to be the opposite of what you want per the function name?

    Miscellaneous Feedback

    updateFilterCounts

    Prefer early returns (i.e. in your checkboxes.forEach you could do if (!label) { return; } instead.

    Doing [every, element, of, another, array, but, one].includes(valueFromThatOtherArray) is pretty weird. Just do if (groupId !== "subject").

    I think this could be tighter:

    const activeEl = document.activeElement; let activeId = null; if (activeEl && activeEl.tagName === 'INPUT' && activeEl.parentElement === container) { activeId = activeEl.id; } 

    Leveraging short-circuiting means that activeId will either be false, undefined, or the actual ID you're looking for by the end of this. All of those are fine for your use case.

    const activeEl = document.activeElement; const activeId = activeEl?.tagName === "INPUT" && activeEl?.parentElement === container && activeEl?.id; 

    I'd also move this code down to the section that actually uses it, since right now the declaration and usage are pretty far separated (and don't, as far as I can tell, depend on anything that happens in between).

    I'm confused why you then use container.querySelector(#${activeId}). It seems like activeEl should already be this element, no? Why not just use activeEl.focus()? Then you don't even need activeId, you would just write

    if (activeEl?.tagName === 'INPUT' && activeEl?.parentElement === container) { activeEl.focus(); } 

    If you do need activeId for a reason I can't see, why not use document.getElementById instead of querySelector since you have the ID?

    updateGallery

    filtered should be const not let.

    You've once again just re-implemented matchesCandidate:

    const filtered = imagesData.filter( entry => groups.every( groupName => groupName in activeFilters && activeFilters[groupName].length > 0 && activeFilters[groupName].some(filter => matchesCandidate(filter, groupName, entry)))); 
    \$\endgroup\$

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.