molily: position:fixed und Anker

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

  1. Hi,

    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.

    Was spricht gegen <a name="anker" style="position:relative; top:-10em;"></a>?

    bei top die Höhe des fixierten Bereichs angeben plus ein bißchen Abstand.

    cu,
    Andreas

    --
    MudGuard? Siehe http://www.mud-guard.de/
    1. Was spricht gegen <a name="anker" style="position:relative; top:-10em;"></a>?

      Nichts. Das ist eine von fünf CSS-Lösungen, die der Artikel abgesehen von der Javascript-Lösung vorstellen wird bzw. schon vorstellt http://home.t-online.de/home/dj5nu/css-position-fixed.html#css2. ;) Aber danke für den Hinweis.

  2. hi,

    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.

    das würde doch dann heissen, dass <a href="#anker1"> nach deiner definition gar kein anker wäre, weil eben die zieladresse ohne anker _leer_ wäre? (oder setzt javascript beim auslesen des href-attributes automatisch den aktuellen dokumentnamen davor, falls dieser nicht angegeben ist?)

    gruss,
    wahsaga

    1. 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.

      das würde doch dann heissen, dass <a href="#anker1"> nach deiner definition gar kein anker wäre, weil eben die zieladresse ohne anker _leer_ wäre? (oder setzt javascript beim auslesen des href-attributes automatisch den aktuellen dokumentnamen davor, falls dieser nicht angegeben ist?)

      Ja, genau. [Linkelementobjekt].href beinhaltet bei einem Link mit href="#anker" die volle Adresse des gegenwärtigen Dokuments. Damit ist die Zieladresse des Links ohne Anker problemlos auslesbar.

      http://home.t-online.de/home/dj5nu/fanhost/css-posfixed-javascript.html zeigt das auch: Link Nummer 13 und Link Nummer 24 sind dokumentinterne Links der Form <a href="#textlink">...</a>. Beide sollten den Event-Handler bekommen (sie werden rot eingefärbt, wenn es der Browser erlaubt). Rechts die Debugausgabe:

      Vergebe an: 13/24
      Linktext: #testlink
      -- hyperlinks[i].href: http://home.t-online.de/home/dj5nu/fanhost/css-posfixed-javascript.html#testlink

      Das ist die Eigenschaft href. Sie enthält die volle Adresse samt Anker (im Falle von Opera 7).

      -- hyperlinks[i].hash: #testlink

      Das ist die Eigenschaft hash, sie enthält nur den Anker.

      -- linkurl: http://home.t-online.de/home/dj5nu/fanhost/css-posfixed-javascript.html
      -- documenturl: http://home.t-online.de/home/dj5nu/fanhost/css-posfixed-javascript.html

      linkurl ist die Zieladresse des Links ohne Anker,
      documenturl die Adresse des aktuellen Dokuments ohne Anker.
      Beide stimmen überein und hash ist gefüllt, damit ist der Link ein dokumentinterner und der Event-Handler wird vergeben.

      In der Tabelle unter dem »Testlink« wird noch einmal veranschaulicht, wie die Eigenschaften des Linkelementobjekts aussehen:
      testlink          http://home.t-online.de/home/dj5nu/fanhost/css-posfixed-javascript.html#testlink
      testlink.href     http://home.t-online.de/home/dj5nu/fanhost/css-posfixed-javascript.html#testlink
      Beide liefern die volle URL (im Falle von Opera 7, Netscape 4, Gecko und MSIE mit Anker, im Falle von Opera 5-6 ohne).

      Ich habe übrigens die neue Version des Artikels hochgeladen, unter http://home.t-online.de/home/dj5nu/css-position-fixed.html#javascript-autoinit (etwas herunterscrollen) ist eine Tabelle, die eine Übersicht bietet, was die Browser als href-Eigenschaft bei Links zu Ankern zurückgeben.