tischlerei-staffenberger/js/script.js
mrwhiski b74dd3744e Fix SEO/a11y issues, add Impressum UID, stop tracking raw photos
- Use absolute og:image URLs and add missing og:description on Impressum
- Load Google Fonts via <link> instead of blocking @import
- Prevent default on shop request button to avoid stray URL hash
- Guard optimize-images.js against silent filename collisions
- Add real Umsatzsteuer-Identifikationsnummer (UID) to Impressum
- Correct README's shop description to match actual empty state
- Stop tracking images/fotowebsite/ (raw, uncompressed source photos)
  since only images/optimized/ is served by the site
2026-08-25 21:48:57 +02:00

215 lines
7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Tischlerei Staffenberger - Main JavaScript
* Handles navigation active state, gallery filtering, and lightbox on Bilder page.
*/
(function () {
'use strict';
/**
* Set active state on the current page's nav link based on URL pathname.
*/
function setActiveNav() {
var navLinks = document.querySelectorAll('.main-nav a');
var path = window.location.pathname;
var page = path.split('/').pop() || 'index.html';
navLinks.forEach(function (link) {
var href = link.getAttribute('href');
if (!href) return;
var linkPage = href.split('/').pop() || 'index.html';
if (linkPage === page || (page === '' && linkPage === 'index.html')) {
link.classList.add('active');
} else {
link.classList.remove('active');
}
});
}
/**
* Gallery filter: show/hide items by data-category.
*/
function initGalleryFilter() {
var filters = document.querySelectorAll('.gallery-filters button');
var items = document.querySelectorAll('.gallery-item');
if (!filters.length || !items.length) return;
filters.forEach(function (btn) {
btn.addEventListener('click', function () {
var category = this.getAttribute('data-filter');
// Update active button
filters.forEach(function (b) {
b.classList.toggle('active', b === btn);
});
// Show/hide items
items.forEach(function (item) {
var itemCategory = item.getAttribute('data-category');
var show = category === 'all' || itemCategory === category;
item.style.display = show ? '' : 'none';
});
});
});
}
/**
* Lightbox on Bilder page: open on gallery image click, navigate with swipe / arrow
* keys / prev+next buttons, close via X, backdrop or ESC.
* Respects the active gallery filter – only visible items are part of the sequence.
*/
function initLightbox() {
var lightbox = document.getElementById('lightbox');
var lightboxImg = document.getElementById('lightbox-img');
if (!lightbox || !lightboxImg) return;
var closeBtn = lightbox.querySelector('.lightbox-close');
var backdrop = lightbox.querySelector('.lightbox-backdrop');
var prevBtn = lightbox.querySelector('.lightbox-prev');
var nextBtn = lightbox.querySelector('.lightbox-next');
var currentIndex = 0;
var sequence = []; // visible gallery links at the time the lightbox was opened
var lastFocused = null; // element to restore focus to on close
function getFocusableEls() {
return [prevBtn, nextBtn, closeBtn].filter(function (el) {
return el && !el.hidden;
});
}
function onFocusTrap(e) {
if (e.key !== 'Tab') return;
var focusable = getFocusableEls();
if (!focusable.length) return;
var first = focusable[0];
var last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
function getVisibleLinks() {
return Array.from(document.querySelectorAll('.gallery-item'))
.filter(function (item) { return item.style.display !== 'none'; })
.map(function (item) { return item.querySelector('.gallery-image-link'); })
.filter(Boolean);
}
function showImage(index) {
var link = sequence[index];
if (!link) return;
currentIndex = index;
lightboxImg.src = link.getAttribute('data-lightbox-src') || link.getAttribute('href');
lightboxImg.alt = link.getAttribute('data-lightbox-alt') || '';
var multiple = sequence.length > 1;
if (prevBtn) prevBtn.hidden = !multiple;
if (nextBtn) nextBtn.hidden = !multiple;
}
function navigate(dir) {
showImage((currentIndex + dir + sequence.length) % sequence.length);
}
function openLightbox(index, trigger) {
sequence = getVisibleLinks();
lastFocused = trigger || document.activeElement;
showImage(index);
lightbox.classList.add('is-open');
document.addEventListener('keydown', onKeydown);
var focusable = getFocusableEls();
if (focusable.length) focusable[0].focus();
}
function closeLightbox() {
lightbox.classList.remove('is-open');
document.removeEventListener('keydown', onKeydown);
if (lastFocused && typeof lastFocused.focus === 'function') lastFocused.focus();
lastFocused = null;
}
function onKeydown(e) {
if (e.key === 'Escape') closeLightbox();
if (e.key === 'ArrowRight') navigate(1);
if (e.key === 'ArrowLeft') navigate(-1);
onFocusTrap(e);
}
// Touch swipe
var touchStartX = 0;
lightbox.addEventListener('touchstart', function (e) {
touchStartX = e.changedTouches[0].clientX;
}, { passive: true });
lightbox.addEventListener('touchend', function (e) {
var dx = e.changedTouches[0].clientX - touchStartX;
if (Math.abs(dx) > 50) navigate(dx < 0 ? 1 : -1);
}, { passive: true });
// Open on gallery click
document.querySelectorAll('.gallery-image-link').forEach(function (link) {
link.addEventListener('click', function (e) {
e.preventDefault();
var visibles = getVisibleLinks();
var idx = visibles.indexOf(link);
openLightbox(idx >= 0 ? idx : 0, link);
});
});
if (prevBtn) prevBtn.addEventListener('click', function () { navigate(-1); });
if (nextBtn) nextBtn.addEventListener('click', function () { navigate(1); });
if (closeBtn) closeBtn.addEventListener('click', closeLightbox);
if (backdrop) backdrop.addEventListener('click', closeLightbox);
}
/**
* "Request" buttons on shop page – open mailto with product name in subject.
*/
function initShopRequests() {
var requestBtns = document.querySelectorAll('.product-card .btn[data-product]');
requestBtns.forEach(function (btn) {
btn.addEventListener('click', function (e) {
e.preventDefault();
var product = this.getAttribute('data-product');
if (product) {
// Open mail with subject line (no real checkout) – replace email with your address
var mailto = 'mailto:info@tischlerei-staffenberger.at?subject=Anfrage%20Produkt%3A%20' + encodeURIComponent(product);
window.location.href = mailto;
}
});
});
}
/**
* Shop empty state: show message when no products are present.
*/
function initShopEmptyState() {
var emptyMsg = document.getElementById('shop-empty-message');
var grid = document.getElementById('shop-grid');
if (!emptyMsg || !grid) return;
var hasCards = grid.querySelectorAll('.product-card').length > 0;
emptyMsg.hidden = hasCards;
}
/**
* Run on DOM ready.
*/
function init() {
setActiveNav();
initGalleryFilter();
initLightbox();
initShopRequests();
initShopEmptyState();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();