0

In the text of the book, links to notes. Notes are found at the bottom of the book.

book text<a href="#n1" name="r1">[1]</a> book text 
book text text<a href="#n2" name="r2">[2]</a> book text 
book text book text<a href="#n3" name="r3">[3]</a> book text..

Notes:
<a name="n1" href="#r1">1</a> Here is the text of note 1
<a name="n2" href="#r2">2</a> Here is the text of note 2
<a name="n3" href="#r3">3</a> Here is the text of note 3
...

I would like the text of the notes to be displayed additionally when hovering over the link in the text.

I don't want to duplicate notes in the text via title, data-title, div.. . Is there an easy way to do this with css3 / html5 / pure js?

Carsten Massmann
  • 26,510
  • 2
  • 22
  • 43
Arsen
  • 131
  • 1
  • 7

2 Answers2

1

If you can use libraries in your project, I'd suggest using Tippy.js or something similiar.

In your case I'd add classes to the link and note markup for easier use with JavaScript, resulting in something like this:

book text<a href="#n1" name="r1" class="reference">[1]</a> book text 
book text text<a href="#n2" name="r2" class="reference">[2]</a> book text 
book text book text<a href="#n3" name="r3" class="reference">[3]</a> book text..

And for the notes I would also put the whole text in your tag, to make it easier to get the text out of each one:

<a name="n1" href="#r1" class="referencedNote">1 Here is the text of note 1</a>
<a name="n2" href="#r2" class="referencedNote">2 Here is the text of note 2</a> 
<a name="n3" href="#r3" class="referencedNote">3 Here is the text of note 3</a>

Now, if you'll only ever have one referencedNote for each reference, you can just easily loop through them and initialize Tippy.js for your tooltips:

let references = document.getElementsByClassName("reference");
let referencedNotes = document.getElementsByClassName("referencedNote");

for(i = 0; i < references.length; i++){

    tippy(references[i], {
      content: referencedNotes[i].innerHTML
    });

}

This is how I would go about it, I hope it can fit your needs as well.

etlaM
  • 131
  • 1
  • 1
  • 9
  • Thanks etlaM. But not exactly what I need. Books for local reading in a browser, without internet connection. It is advisable to place the script in the body of the page. – Arsen Feb 28 '21 at 17:50
1

This should do what you need:

const toolTipHover=ev=>{
 const tt=document.getElementById("tooltip");
 if(ev.type=="mouseover") {
  if(ev.target.tagName=="A"){
   let href=ev.target.getAttribute("href");
   if (href.length>1 && href.slice(0,2)=="#n") { //  only for internal links ...
    let a=document.querySelector("[name="+href.substr(1)+"]")
    if (a) { // and only if these are actually found on the page
     tt.innerHTML=a.textContent+": "+a.nextSibling.textContent
     tt.style.left=Math.min(ev.pageX,window.pageXOffset+window.innerWidth-tt.offsetWidth)+"px";
     tt.style.top = (ev.pageY+15 > window.pageYOffset+window.innerHeight-tt.offsetHeight 
       ? ev.pageY-tt.offsetHeight
       : ev.pageY+15)+"px";
    }
   }
  }
 } else {
  tt.style.left="-1000px";  
 }
} 
["mouseover","mouseout"].map(ev=>document.body.addEventListener(ev,toolTipHover));
.space{height:100px}
#tooltip {position:absolute; width:200px; left:-1000px;
          background-color: #fff;
          padding:6px; border:1px solid gray}
<pre>
book text<a href="#n1" name="r1">[1]</a> book text.
     Here is an external <a href="https://google.com">link</a> that should not fire.
book text text<a href="#n2" name="r2">[2]</a> book text 
book text book text<a href="#n3" name="r3">[3]</a> book text..
<div class="space"></div>
Notes:
<a name="n1" href="#r1">1</a> Here is the text of note 1
<a name="n2" href="#r2">2</a> Here is the text of note 2
<a name="n3" href="#r3">3</a> Here is the text of note 3
<div class="space"></div>
<div class="space"></div>
Here is some more Text to demonstrate what happens border cases <a href="#n3" name="r3">[3]</a>. And some more text.
<a href="#">back to the top</> this link should not fire ...
</pre>
<div id="tooltip"></div>

I added a div with position:absolute. Its initial position is off the screen (1000 pixels to the left). Whenever the mouse is moved over an A tag the hover() event function will step into action:

  • When the event was "mouseover" it will search for the corresponding note text, place it in the tooltip and position the tooltip div close to the current mouse position.
  • If the event was "mouseout" the position of the tooltip-div will be set off-screen again.

In my snippet I used .nextSibling to find the next text node. This works for the sample data given but will not be very helpfull, if the footnote is slightly longer and contains other HTML elements like spans or links. It would be better if you used a <p> or <div> element after the footnote-<a> for the actual footnote text. These elements would then be found more reliably by using nextElementSibling (see here for further reference).

After looking at OP's jsfiddle I changed my snippet and used for the vertical positioning the following:

tt.style.top = (ev.pageY+15 > window.pageYOffset+window.innerHeight-tt.offsetHeight 
       ? ev.pageY-tt.offsetHeight
       : ev.pageY+15)+"px";

I also forked the fiddle, see here.

Carsten Massmann
  • 26,510
  • 2
  • 22
  • 43
  • Thanks cars10m. Very good. But. If the link is at the right edge of the page, the tooltip is not fully visible and horizontal scrolling appears. Is the content hint width possible? The script does not work in IE 11.0. After the footnote - any element can be used for the actual footnote text. I only posted the basic code on purpose. – Arsen Feb 28 '21 at 18:46
  • Feel free to adapt the code to your own needs. It is all there and ready to be modified. Remember: Stackoverflow is not a free coding service ;-) – Carsten Massmann Feb 28 '21 at 19:10
  • The methods I used in my snippet require a "modern" browser. If you insist on supporting the IE11 then using a library like jQuery is probably the most reliable way forward. – Carsten Massmann Feb 28 '21 at 19:13
  • Got better with links on the right edge. I can see the tooltip is also showing in the bottom backlinks. Shows the text from the link to the end of the line. Is it possible to bind the tooltip to a class and not a tag? – Arsen Mar 01 '21 at 08:29
  • 1
    You *could* work with classes to decide which links should fire the tooltip, but alternatively you can also look at the href itself: I changed my code, so it will now only fire on links having href starting with "#n". – Carsten Massmann Mar 01 '21 at 18:04
  • Thanks cars10m. "#n" - better than class? Another moment with the bottom location. [jsfiddle](http://jsfiddle.net/3zxhj4dy/) Third link at the bottom. The tooltip is not fully visible. – Arsen Mar 01 '21 at 19:12
  • There is no "better" or "worse". The "#n" was already there and saves me (or you) from changing the markup. A class based solution is also possible of course. With respect to the bottom problem: you could do something similar to what I have done with the X-position: `Math.min(ev.pageY,window.innerHeight-100)`. A slight problem here is of course that you don't really know the height of the tooltip. – Carsten Massmann Mar 01 '21 at 19:45
  • Understood with "#n". With respect to the bottom problem: tried - `Math.min(ev.pageY+15,window.innerHeight-tt.offsetHeight)`. I have specified the height of the tooltip via the `offsetHeight` property.. [jsfiddle](http://jsfiddle.net/sygcmnv0/). Is this entry correct? – Arsen Mar 01 '21 at 21:33