130 lines
4.9 KiB
JavaScript
130 lines
4.9 KiB
JavaScript
const text = (selector, value) => {
|
|
const element = document.querySelector(selector);
|
|
if (element) element.textContent = value ?? "";
|
|
};
|
|
|
|
const setLink = (selector, href, label) => {
|
|
const element = document.querySelector(selector);
|
|
if (!element) return;
|
|
element.href = href;
|
|
element.textContent = label;
|
|
};
|
|
|
|
const addText = (parent, tag, className, value) => {
|
|
const element = document.createElement(tag);
|
|
element.className = className;
|
|
element.textContent = value;
|
|
parent.append(element);
|
|
return element;
|
|
};
|
|
|
|
const renderBusiness = (business) => {
|
|
document.title = business.metaTitle;
|
|
document.querySelector('meta[name="description"]').content =
|
|
business.metaDescription;
|
|
|
|
text(".brand-name", business.name);
|
|
text(".footer-brand", business.name);
|
|
text(".header-call-label", business.headerCallLabel);
|
|
text(".header-phone", business.phoneDisplay);
|
|
text("[data-nav-services]", business.navigation.services);
|
|
text("[data-nav-about]", business.navigation.about);
|
|
text("[data-nav-contact]", business.navigation.contact);
|
|
|
|
const brand = document.querySelector(".brand");
|
|
brand.setAttribute("aria-label", business.homeLabel);
|
|
const logo = document.querySelector(".brand-logo");
|
|
if (business.logo) {
|
|
logo.src = business.logo;
|
|
logo.alt = business.logoAlt || business.name;
|
|
logo.hidden = false;
|
|
}
|
|
|
|
document.querySelectorAll("[data-phone-link]").forEach((link) => {
|
|
link.href = `tel:${business.phone.replace(/[^\d+]/g, "")}`;
|
|
});
|
|
setLink(
|
|
".hero-primary",
|
|
`tel:${business.phone.replace(/[^\d+]/g, "")}`,
|
|
business.hero.primaryAction,
|
|
);
|
|
setLink(".hero-secondary", "#services", business.hero.secondaryAction);
|
|
text(".hero-eyebrow", business.hero.eyebrow);
|
|
text(".hero-title", business.hero.title);
|
|
text(".hero-description", business.hero.description);
|
|
text(".hero-proof", "");
|
|
const proof = document.querySelector(".hero-proof");
|
|
business.highlights.forEach((item) => addText(proof, "span", "", item));
|
|
|
|
const heroImage = document.querySelector(".hero-image");
|
|
heroImage.src = business.hero.image;
|
|
heroImage.alt = business.hero.imageAlt;
|
|
|
|
text(".services-eyebrow", business.servicesSection.eyebrow);
|
|
text(".services-title", business.servicesSection.title);
|
|
text(".services-intro", business.servicesSection.description);
|
|
const serviceList = document.querySelector(".service-list");
|
|
business.services.forEach((service, index) => {
|
|
const item = document.createElement("article");
|
|
item.className = "service-item";
|
|
addText(item, "span", "service-index", String(index + 1).padStart(2, "0"));
|
|
addText(item, "h3", "", service.name);
|
|
addText(item, "p", "", service.description);
|
|
serviceList.append(item);
|
|
});
|
|
|
|
text(".about-marker .about-number", business.about.number);
|
|
text(".about-eyebrow", business.about.eyebrow);
|
|
text(".about-title", business.about.title);
|
|
text(".about-description", business.about.description);
|
|
setLink(".about-link", "#contact", business.about.linkLabel);
|
|
text(".about-quote", business.about.quote);
|
|
text(".about-quote-byline", business.about.quoteByline);
|
|
|
|
text(".contact-eyebrow", business.contact.eyebrow);
|
|
text(".contact-title", business.contact.title);
|
|
text(".contact-description", business.contact.description);
|
|
const contactPhone = document.querySelector(".contact-phone");
|
|
contactPhone.href = `tel:${business.phone.replace(/[^\d+]/g, "")}`;
|
|
contactPhone.textContent = business.contact.phoneLabel;
|
|
setLink(".contact-email", `mailto:${business.email}`, business.email);
|
|
text(".contact-location-label", business.contact.locationLabel);
|
|
text(".contact-address", business.location);
|
|
text(".contact-hours", business.hours);
|
|
text(".footer-location", business.location);
|
|
text(".footer-copyright", `© ${new Date().getFullYear()} ${business.name}`);
|
|
};
|
|
|
|
const menuToggle = document.querySelector(".menu-toggle");
|
|
const navigation = document.querySelector(".main-nav");
|
|
menuToggle.addEventListener("click", () => {
|
|
const isOpen = menuToggle.getAttribute("aria-expanded") === "true";
|
|
menuToggle.setAttribute("aria-expanded", String(!isOpen));
|
|
menuToggle.setAttribute(
|
|
"aria-label",
|
|
isOpen ? "Open navigation" : "Close navigation",
|
|
);
|
|
navigation.classList.toggle("is-open", !isOpen);
|
|
});
|
|
|
|
navigation.addEventListener("click", (event) => {
|
|
if (event.target.closest("a")) {
|
|
navigation.classList.remove("is-open");
|
|
menuToggle.setAttribute("aria-expanded", "false");
|
|
menuToggle.setAttribute("aria-label", "Open navigation");
|
|
}
|
|
});
|
|
|
|
fetch("data/business.json")
|
|
.then((response) => {
|
|
if (!response.ok)
|
|
throw new Error(`Could not load business config: ${response.status}`);
|
|
return response.json();
|
|
})
|
|
.then(renderBusiness)
|
|
.catch((error) => {
|
|
console.error(error);
|
|
document.body.innerHTML =
|
|
'<main class="config-error"><h1>Business details could not be loaded.</h1><p>Run this site from a local web server and check data/business.json.</p></main>';
|
|
});
|