Skip to content

Instantly share code, notes, and snippets.

@deanebarker
Last active October 18, 2024 23:18
Show Gist options
  • Select an option

  • Save deanebarker/8a205c360386661724c7ab62ef754e65 to your computer and use it in GitHub Desktop.

Select an option

Save deanebarker/8a205c360386661724c7ab62ef754e65 to your computer and use it in GitHub Desktop.
Simple code to enable mouseover tooltips

This is the simplest form of a mouseover "tooltip" UI that I could come up with.

You can see it used on local hyperlinks at https://deanebarker.net/.

Note: this is NOT a "library." This is starter code. There are no settings or configuration (meaning: everything is hard-coded) nor has it been designed to be extensible -- you are expected copy this code and change it. It's essentially just an example.

To use

  1. Somehow form an array of elements to which you want to have tips attached
  2. Implement a function to return the HTML of a tip. It will be passed the entire element that triggered the tip. Do whatever you want (read from an attribute, do a lookup on the HREF, whatever). Return the HTML content for the tip.
  3. Pass both to bindTips

Example:

// 1. Get the elements that need tips
var links = document.querySelectorAll('a');

// 2. Create a function to return the tip content
function getTipContent(el) {
  var href = el.getAttribute('href');
  return href + " is a great URL!!!";
}

// 3. VOILA!
bindTips(links, getTipContent);

But I don't want to show tooltips on [insert criteria here]...

You control the collection passed to bindTips. Form that collection however you want.

But I want them to look different...

There is literally one element you need to style. The CSS is right there. Go nuts.

But I just want to display the content of an attribute...

getTipContent is passed the entire element. Do whatever you want.

<a href="whatever" data-tooltip="This is a really cool website"/>
function getTipContent(el) {
  return el.getAttribute('data-tooltip');
}

But I want to retieve the tooltip from [insert hare-brained idea here]...

Again, do whatever you want in getTipContent. It's designed to show some placeholder text until that function returns. I wouldn't dilly-dally or anything, but reasonable I/O isn't going to look weird (I do a background HTTP call in my use case; it feels fine to me).

Why don't you have a settings/config object?

Again, this is not a plugin/module/extension/library. I wrote this for me and my one and only use case. There will never be an automated update to this code via npm or yarn or whatever.

Copy the code, do whatever you want without fear.

div.tip
{
padding: 1em;
border: solid 1px rgb(200,200,200);
background: rgb(240,240,240);
}
function bindTips(selector, callback) {
selector.forEach(el => {
el.addEventListener("mouseenter", (e) => openTip(e, callback));
el.addEventListener("mouseleave", closeTip);
});
}
async function openTip(e, callback) {
var div = document.createElement("div");
div.classList.add("tip")
div.style.position = "fixed";
div.style.top = (e.y+20) + "px";
div.style.left = (e.x+20) + "px";
div.innerHTML = "Loading...";
document.body.appendChild(div);
// Now load the actual content...
div.innerHTML = await callback(e.target);
}
function closeTip(e) {
// There should only ever be one on the page at any time
// But, just in case...
Array.from(document.querySelectorAll("div.tip")).forEach(el => {
document.body.removeChild(el);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment