Im Zuge der Javascript-Vermeidung beim Redesign von pc-kombo habe ich Tabs mit CSS umgesetzt. Das Tab ist ein a
-Element und zeigt auf den #Tabinhalt, also dessen id. Wenn auf das Tab geklickt wurde kann dann per CSS der Tabinhalt als :target
eingeblendet werden. Im Grunde wie hier von Tim Perry erklärt.
Ich wollte aber nun auch verhindern, dass der Browser nach dem Klick nach unten scrollt. Das wäre das normale Verhalten, wenn auf einen Link mit # geklickt wird. Hier aber stört das. Ich kam zuerst auf einen anderen Ansatz als im später gefundenen und verlinkten Artikel:
var tabs = document.querySelectorAll(".tab-item a");
var scrollX = 0;
var scrollY = 0;
var blockScroll = false;
for (var i=0;i < tabs.length; i++) {
tabs[i].addEventListener("click", function(e) {
scrollX = window.pageXOffset;
scrollY = window.pageYOffset;
blockScroll = true;
});
};
window.addEventListener('hashchange', function() {
window.scrollTo(scrollX, scrollY);
}, false);
window.addEventListener('scroll', function() {
if (blockScroll) {
blockScroll = false;
window.scrollTo(scrollX, scrollY);
}
});
Demo hier.
Das Scrollen wird hier in drei Schritten und zwei Wegen blockiert bzw rückgängig gemacht:
- Zuerst wird beim Klick auf den Tab geschaut, wohin der Browser gerade gescrollt hat und das in zwei Variablen gespeichert.
- Wenn danach das
hashchange
-Event getriggert wird, scrollt das zur gespeicherten Position zurück. Das funktionierte oft, manchmal stimmt da aber die Abfolge nicht und es wird wohl verzögert trotzdem zum Tabinhalt gescrollt.
- Deswegen fängt der zweite Ansatz den
scroll
-Event und scrollt auf die Ausgangsposition zurück.
Ja, das sieht ein bisschen gefährlich aus, als könnte man mit dem Code unabsichtlich ein Scrollereignis nach dem Tabwechsel blocken. In meinen Tests (in Firefox und Chromium) passiert das aber nicht, weil das Scroll-Ereignis immer einmal nach dem Klick ausgeführt und geblockt und daraufhin die Variable zurückgesetzt wird.
Der im Artikel vorgeschlagene Ansatz ist ein anderer: Bei Klick mache ein event.preventDefault()
, dann zweimal ein history.pushState()
zum Tabinhalt und gehe einmal zurück. Zweimal und zurück, weil wenn man einfach nur das Event unterdrückt und das target mit pushState() setzt der Browser das ignoriert. Er scrollt dann zwar nicht, aber deckt auch den Tabinhalt nicht auf. Mit dem doppelten Eintrag und einmaligen zurückgehen funktioniert das dagegen einwandfrei, und da flickerfrei besser als mit meinem ursprünglichen Ansatz. Der leicht angepasste Code:
var tabs = document.querySelectorAll(".tab-item a");
for (var i=0;i < tabs.length; i++) {
tabs[i].addEventListener("click", function(e) {
e.preventDefault();
history.pushState({}, "", e.target.getAttribute('href'));
// Update the URL again with the same hash, then go back
history.pushState({}, "", e.target.getAttribute('href'));
history.back();
});
};
Das werde ich zusammen mit einer noch ausstehenden Änderung bald bei Hardwarelisten wie der für Prozessoren anwenden, sodass die Filterauswahl angenehmer zu benutzen ist.