Hallo,
ich überarbeite gerade meinen Artikel über position:fixed und Linkanker/Sprungmarken. Es geht darum, dass am Dokumenoberrand fest positionierte Elemente angesprungene Linkanker überdecken. Dabei stelle ich auch eine JavaScript-Lösung vor, welche grob gesagt über scrollBy() den Fokus verschiebt, damit der Anker wieder unter dem fest positionierten Bereich hervorkommt.
Ich habe eine Funktion geschrieben, die automatisch jedem dokumentinternen Link einen Event-Handler zuweist, welcher beim Anklicken die Fokuskorrektur vornimmt. Die alte Version ist unter http://home.t-online.de/home/dj5nu/css-position-fixed.html#javascript-autoinit zu finden (URL stirbt in Kürze ohne Weiterleitung - bitte nicht bookmarken). Die Funktion durchläuft alle Links im Dokument und bringt in Erfahrung, ob es sich um einen dokumentinternen Link zu einem Anker handelt. Wenn dies der Fall ist, wird dem Link der Event-Handler gegeben.
Meine Theorie zur Überprüfung, ob ein Link zu einem dokumeninternen Anker führt, ist folgendermaßen: Ein Link führt genau dann zu einem dokumentinternen Anker, wenn der Anker des Links gefüllt ist sowie die Zieladresse des Links ohne Anker und Adresse des aktuellen Dokuments ohne Anker übereinstimmen.
Unglücklicherweise liefern verschiedene Browser verschiedene Inhalte von location.href und [Linkelementobjekt].href, mal mit Anker am Ende und mal ohne. Daher müssen B und C erst durch Fallunterscheidungen konstruiert werden.
Die momentane Implementation der Funktion sieht folgendermaßen aus:
1 /* link_scrollup() korrigiert den Fokus beim Klicken
2 * eines Links zu einem dokumentinternen Anker */
3 function link_scrollup () {
4 if (window.setTimeout && window.scrollBy)
5 window.setTimeout('window.scrollBy(0, -55)', 100);
6 }
7
8 /* initialise() wird beim Laden des Dokuments ausgeführt */
9 function initialise () {
10
[...unwichtiger Kommentar, der nur im Kontext des Artikel verständlich ist...]
14
15 /* Korrigiere Fokus beim Laden des Dokuments */
16 if (location.hash) window.scrollBy(0, -55);
17
18 /* Weise allen Links zu dokumentinternen Ankern den Event-Handler zu */
19
20 var hyperlinks,linkcount, linkurl,documenturl, hashposition, pattern;
21
22 hyperlinks=document.links;
23 linkcount=hyperlinks.length;
24
25 /* Durchlaufe alle Hyperlinks des Dokuments */
26 for (var i=0; i<linkcount; i++) {
27
28 /* Gehe zum nächsten Link über, falls die hash-Eigenschaft
29 * leer ist oder nur # enthält */
30 if (!hyperlinks[i].hash || hyperlinks[i].hash=='#') continue;
31
32 /* Konstruiere die Zieladresse des Links ohne Anker */
33 hashposition=hyperlinks[i].href.indexOf("#");
34 if (hashposition>-1)
35 /* href enthält einen Anker, schneide ihn ab */
36 linkurl=hyperlinks[i].href.substring(0, hashposition);
37 else
38 /* href enthält keinen Anker */
39 linkurl=hyperlinks[i].href;
40
41 /* Konstruiere die Adresse des aktuellen Dokuments ohne Anker */
42 hashposition=location.href.indexOf("#");
43 if (hashposition>-1)
44 /* Die aktuelle Adresse enthält einen Anker, schneide ihn ab */
45 documenturl=location.href.substring(0, hashposition);
46 else
47 /* Die aktuelle Adresse enthält keinen Anker */
48 documenturl=location.href;
49
50 /* Falls es sich einen Link auf ein lokalens Dokument handelt
51 * (file-Protokoll), vereinheitliche die Schreibweise,
52 * ersetze file:// durch file://localhost/ */
53 if (linkurl.indexOf('file://')==0) {
54 if (typeof(pattern)=='undefined') pattern=new RegExp('file:///');
55 linkurl=linkurl.replace(pattern, 'file://localhost/');
56 documenturl=documenturl.replace(pattern, 'file://localhost/');
57 }
58
59 /* Vergleiche das Linkziel mit der aktuellen Adresse */
60 if (linkurl==documenturl)
61 /* Der Link führt zum aktuellen Dokument und hat einen Anker, damit
62 * sind alle Bedingungen erfüllt, vergebe den Event-Handler */
63 hyperlinks[i].onclick=link_scrollup;
64
65 }
66
67 }
68
69 /* Starte die Funktion Funktion initialise beim Laden des Dokuments */
70 window.onload=initialise;
(Die Zahl 55 in scrollBy(0, -55) ist die Höhe des fest positionierten Bereichs plus ein wenig Puffer, dieser Wert ist letztlich variabel.)
Ich weiß, der Code ist auf den ersten Blick vertrackt, aber die Vorgehensweise sollte durch die Kommentare zumindest grob ersichtlich sein. Im Artikel werde ich auch noch eine weitere Erklärung hinzufügen.
Was mich jetzt interessiert: Ist die Umsetzung so effizient, ließe es sich besser lösen? Es erscheint mir insgesamt aufwändig, ich wüsste aber nicht, wo weiter vereinfacht werden könnte. Ich möchte auch möglichst alle Fälle abdecken.
Eigentlich werden nur position:fixed-Fähige Browser angepeilt, mangels Möglichkeit, diese Unterstützung direkt per JavaScript in Erfahrung zu bringen, sind momentan auch nicht-position:fixed-fähige Browser von der Funktion betroffen. Sie funktioniert meinen Tests zufolge ab Netscape 4, MSIE 5, Opera 5 und Mozilla M16. MSIE 4 sollte es theoretisch auch können, ich konnte es nicht prüfen.
Einzig bei lokalen file:-Links hatte ich das Problem, dass der Pfadname im Link (bspw. <a href="file://.../VERZEICHNIS/dokument.html#anker">) exakt dem Verzeichnisnamen entsprechen muss, der in der Adressleiste steht. Das ist unter Windows problematisch, da beispielsweise file://localhost/c:/VERZEICHNIS/dokument.html und file://localhost/c:/verzeichnis/dokument.html auf dasselbe Dokument verweisen, location.href aber jeweils anders ist.
Wenn nun ein Dokument mit der Adresse file://localhost/c:/VERZEICHNIS/dokument.html aufgerufen wird und dort ein absoluter Link zu einem dokumentinternen Anker mit der Adresse file://localhost/c:/verzeichnis/dokument.html#anker existiert, erkennt meine Funktion nicht, dass es sich um einen dokumentinternen Anker handelt. Sicherlich, dieser Fall kommt extrem selten vor.
Meine Testseite mit Debug-Ausgabe (dafür ist DOM nötig) habe ich unter http://home.t-online.de/home/dj5nu/fanhost/css-posfixed-javascript.html online gestellt. Die Links 13, 17 und 24 sollten als dokumentinterne Links identifiziert werden (und 14 und 15, wenn das Dokument im entsprechenden lokalen Pfad liegt).
Mathias