Adding hovercards to my website
Published on under the IndieWeb category.

I love how Wikipedia shows you a preview of a page when you hover over a link to another page in a wiki entry. This makes Wikipedia more navigable for me, particularly if I am only looking for a definition of a term (text that appears in the preview) to help me better understand the contents on a page. This got me thinking about adding a hover feature to my website that would show you a preview of links that appear in my blog posts.
The IndieWeb wiki already had an entry on this topic. The IndieWeb wiki refers to tooltips that appear when you hover over a link as a "hovercard". There has been a lot of brainstorming on this topic, and reading over the page made me realise how much is possible. I decided to start small and simple. I set a goal to add hovercards to the links in my blog post. Hovercards would show:
- The title of the linked page.
- A short description of the linked page (if one can be found).
- An image representing the page (if one can be found).
Implementing hovercards
My hovercard implementation consists of two parts: the JavaScript necessary to load a hovercard and the back-end code that lets me retrieve a hovercard.
JavaScript implementation
I wrote a short script that adds "mouse over" (hover) and focus events to all of the links in HTML article
tags on my website. These events call a function that looks for the href
value of a link in a JSON object. This object maps URLs on the page to a title, description, and image. If a link is found, a new box is created that appears directly below the link. This box contains information from the JSON object. If a user hovers away from the link, or removes focus from the link with their keyboard, the tooltip will disappear.
Here is a short video that shows this behaviour in action:
When I hover over the links in the post, the hovercard appears. When I hover away from a link, the hovercard disappears.
I have created a project on GitHub called hovercard.js
that contains all of the code for hovercards. It comes with a default JSON object that stores a single hovercard for a W3C link. If you create a link to https://www.w3.org/community/sustyweb/
in an article
tag on a page with the script loaded, you will be able to see the hovercard in action. You can add whatever links you want into the JSON object.
Back-end implementation
You may be wondering: how do you get the JSON object with the information about links? I handle this on the back-end, in my static site generator, the tool I use to create HTML documents for all of the pages on my site.
When my static site generator creates a page on my site, it now looks through all links it can find in the contents of a blog post. If a link is found, I run the indieweb_utils.get_reply_context
function to retrieve information about the link. Here is an adapted excerpt of how I get reply contexts:
import indieweb_utils
hovercards = {}
url = "https://github.com/capjamesg/indieweb-utils"
try:
get_context = indieweb_utils.get_reply_context(url)
except:
continue
if not get_context:
continue
hovercards[url.rstrip("/")] = {
"title": get_context.name,
"description": get_context.description,
"photo": get_context.photo,
}
print(hovercards)
When run, this code will generate a hovercard for the contents of the URL https://jamesg.blog/2022/10/12/indieweb-utils-v-0-4-0/
. I run this function on every link in every article. Then, I take the hovercards dictionary, turn it into a JSON object using Python's built in JSON
library, and put the new JSON object on a web page.
This approach means I don't have to request all the URLs in a post directly on the front-end, which is inefficient and would slow down every blog post that contained a link in its article body. I can generate reply contexts when I build my site, then put them only on the page where I am going to use them.
I define the hovercards
JavaScript variable above the hovercard script. This means that I can use my custom list instead of the one provided in my hovercards.js
script. Remember to keep the var hovercards
declaration commented out if you are going to provide your own custom hovercard JSON object on the page.
Here is the generated hovercard item from my code example above:
{
'https://github.com/capjamesg/indieweb-utils': {
'title': 'GitHub - capjamesg/indieweb-utils: Utilities to aid the implementation of various IndieWeb specifications and functionalities. Built with Python.',
'description': '',
'photo': 'https://opengraph.githubassets.com/86ee8a8bacc233bfb629c209342616750493c1bd36b114d53f22d07029735cdb/capjamesg/indieweb-utils'
}
}
We now have a hovercard we can render on the web page using the hovercard.js
file. This hovercard contains a title and photo for the provided link. As I write this, I will make a note to revisit the IndieWeb Utils reply context logic because this link should have a description. (This is the beauty of selfdogfooding: you find issues and ways to improve your work as you go along because you are building something for yourself and using it yourself.)
Conclusion
With my hovercard implementation, you can now preview many of the links that appear in my blog posts. There are some ideas hovering in the back of my mind (pun intended!) that I would like to explore at some point:
- Can I add hovercards to my long blog post list pages (like my IndieWeb post list) to make it easier to navigate the page and decide what to read?
- Can I add definitions from the IndieWeb wiki to jargon terms I use on my site? I have historically not been good at linking to terms on my blog but perhaps this could change.
- How can I improve IndieWeb Utils to make its reply context description logic more effective?
You can try the hovercards out on this post. Hover over any link and if context could be retrieved for the link you should see a hovercard summarising the contents of the link. If you have any feedback on how I can improve my implementation, let me know!
Tagged in indieweb-utils, python, indieweb.
Responses
Comment on this post
Respond to this post by sending a Webmention.
Have a comment? Email me at readers@jamesg.blog.