diff --git a/src/js/about.ts b/src/js/about.ts index 7335e33..85d69d5 100644 --- a/src/js/about.ts +++ b/src/js/about.ts @@ -4,38 +4,39 @@ * Set up event listeners to open and close the about popup. */ const setUpAbout = () => { - const aboutElement: HTMLElement | null = - document.querySelector(".about-text-popup"); + const aboutElement = document.querySelector(".about-text-popup"); const infoButton = document.querySelector(".header-about-icon-container"); - if (infoButton && aboutElement) { - infoButton.addEventListener("click", () => { - aboutElement.style.display = - aboutElement.style.display !== "block" ? "block" : "none"; - }); + if ( + !(aboutElement instanceof HTMLElement) || + !(infoButton instanceof HTMLElement) + ) + return; - // closes window on clicks outside the info popup - window.addEventListener("click", (event: MouseEvent) => { - const clickTarget = event?.target as Element; - if ( - !infoButton.contains(clickTarget) && - aboutElement.style.display === "block" && - !aboutElement.contains(clickTarget) - ) { - aboutElement.style.display = "none"; - infoButton.classList.toggle("active"); - } - }); + infoButton.addEventListener("click", () => { + aboutElement.style.display = + aboutElement.style.display !== "block" ? "block" : "none"; + }); + // closes window on clicks outside the info popup + window.addEventListener("click", (event) => { + if ( + event.target instanceof Element && + !infoButton.contains(event.target) && + aboutElement.style.display === "block" && + !aboutElement.contains(event.target) + ) { + aboutElement.style.display = "none"; + infoButton.classList.toggle("active"); + } + }); + + const aboutClose = document.querySelector(".about-close"); + if (!(aboutClose instanceof HTMLElement)) return; + aboutClose.addEventListener("click", () => { // Note that the close element will only render when the about text popup is rendered. // So, it only ever makes sense for a click to close. - const aboutClose: HTMLAnchorElement | null = - document.querySelector(".about-close"); - if (aboutClose) { - aboutClose.addEventListener("click", () => { - aboutElement.style.display = "none"; - }); - } - } + aboutElement.style.display = "none"; + }); }; export default setUpAbout; diff --git a/src/js/setUpSite.ts b/src/js/setUpSite.ts index d0e11c5..9527d77 100644 --- a/src/js/setUpSite.ts +++ b/src/js/setUpSite.ts @@ -232,36 +232,32 @@ const setUpCitiesLayer = async ( // Set up map to update when city selection changes. const cityToggleElement = document.getElementById("city-dropdown"); - if (cityToggleElement instanceof HTMLSelectElement) { - cityToggleElement.addEventListener("change", async () => { - const cityId = cityToggleElement.value; - const { layer } = cities[cityId]; - if (layer) { - snapToCity(map, layer); - } - }); + if (!(cityToggleElement instanceof HTMLSelectElement)) return; + cityToggleElement.addEventListener("change", async () => { + const cityId = cityToggleElement.value; + const { layer } = cities[cityId]; + if (layer) { + snapToCity(map, layer); + } + }); - // Set up map to update when user clicks within a city's boundary - allBoundaries.addEventListener("click", (e) => { - const currentZoom = map.getZoom(); - if (currentZoom > 7) { - const cityId = e.sourceTarget.feature.properties.id; - cityToggleElement.value = cityId; - const { layer } = cities[cityId]; - if (layer) { - snapToCity(map, layer); - } - } - }); + // Set up map to update when user clicks within a city's boundary + allBoundaries.addEventListener("click", (e) => { + const currentZoom = map.getZoom(); + if (currentZoom <= 7) return; + const cityId = e.sourceTarget.feature.properties.id; + cityToggleElement.value = cityId; + const { layer } = cities[cityId]; + if (layer) { + snapToCity(map, layer); + } + }); - // Load initial city. - const cityId = cityToggleElement.value; - setUpAutoScorecard(map, cities, parkingLayer); - snapToCity(map, cities[cityId].layer); - setScorecard(cityId, cities[cityId]); - } else { - throw new Error("#city-dropdown is not a select element"); - } + // Load initial city. + const cityId = cityToggleElement.value; + setUpAutoScorecard(map, cities, parkingLayer); + snapToCity(map, cities[cityId].layer); + setScorecard(cityId, cities[cityId]); }; /** @@ -277,22 +273,20 @@ const setUpParkingLotsLayer = async ( }, }).addTo(map); + if (window.location.href.indexOf("#lots-toggle") === -1) return parkingLayer; + // If `#lots-toggle` is in the URL, we show buttons to toggle parking lots. - if (window.location.href.indexOf("#lots-toggle") !== -1) { - const toggle: HTMLAnchorElement | null = - document.querySelector("#lots-toggle"); - if (toggle) { - toggle.style.display = "block"; - } - document - .querySelector("#lots-toggle-off") - ?.addEventListener("click", () => { - parkingLayer.removeFrom(map); - }); - document.querySelector("#lots-toggle-on")?.addEventListener("click", () => { - parkingLayer.addTo(map); - }); + const toggle: HTMLAnchorElement | null = + document.querySelector("#lots-toggle"); + if (toggle) { + toggle.style.display = "block"; } + document.querySelector("#lots-toggle-off")?.addEventListener("click", () => { + parkingLayer.removeFrom(map); + }); + document.querySelector("#lots-toggle-on")?.addEventListener("click", () => { + parkingLayer.addTo(map); + }); return parkingLayer; }; diff --git a/src/js/share.ts b/src/js/share.ts index c5e6e79..dca3b76 100644 --- a/src/js/share.ts +++ b/src/js/share.ts @@ -12,41 +12,33 @@ const copyToClipboard = async (value: string): Promise => { }; const switchIcons = (shareIcon: HTMLAnchorElement): void => { - const linkIcon: HTMLAnchorElement | null = shareIcon.querySelector( - "svg.share-link-icon" - ); - const checkIcon: HTMLAnchorElement | null = shareIcon.querySelector( - "svg.share-check-icon" - ); - if (linkIcon && checkIcon) { - linkIcon.style.display = "none"; - checkIcon.style.display = "inline-block"; - setTimeout(() => { - linkIcon.style.display = "inline-block"; - checkIcon.style.display = "none"; - }, 1000); - } + const linkIcon = shareIcon.querySelector("svg.share-link-icon"); + const checkIcon = shareIcon.querySelector("svg.share-check-icon"); + if (!(linkIcon instanceof SVGElement) || !(checkIcon instanceof SVGElement)) + return; + + linkIcon.style.display = "none"; + checkIcon.style.display = "inline-block"; + setTimeout(() => { + linkIcon.style.display = "inline-block"; + checkIcon.style.display = "none"; + }, 1000); }; const setUpShareUrlClickListener = (cityId: CityId): void => { // We put the event listener on `map` because it is never erased, unlike the copy button // being recreated every time the score card changes. This is called "event delegation". - const mapElement: HTMLElement | null = document.querySelector("#map"); - if (mapElement) { - mapElement.addEventListener("click", async (event: MouseEvent | null) => { - const copyButton = event?.target as Element; - if (copyButton) { - const targetElement: HTMLAnchorElement | null = copyButton.closest( - "div.url-copy-button > a" - ); - if (targetElement) { - const shareUrl = determineShareUrl(window.location.href, cityId); - await copyToClipboard(shareUrl); - switchIcons(targetElement); - } - } - }); - } + const mapElement = document.querySelector("#map"); + if (!(mapElement instanceof Element)) return; + mapElement.addEventListener("click", async (event) => { + const copyButton = event.target; + if (!(copyButton instanceof Element)) return; + const targetElement = copyButton.closest("div.url-copy-button > a"); + if (!(targetElement instanceof HTMLAnchorElement)) return; + const shareUrl = determineShareUrl(window.location.href, cityId); + await copyToClipboard(shareUrl); + switchIcons(targetElement); + }); }; export default setUpShareUrlClickListener;