Couple days ago Amr shared a nice website called Boring Rails that resonates with what I believe a simple way to develop web applications that has less JavaScript fatigue.
I tend to minimize JavaScript on my websites to the point that sometimes I remove JavaScript all together like on this blog.
The first article Building GitHub-style Hover cards with StimulusJS and HTML-over-the-wire talks in details what’s wrong with the modern culture of developing web applications and the SPA frenzy going on.
Then the article went to implement hover cards with some tools, I believe that the same feature can be delivered with less code, The following paragraphs will walk you through implementing the same feature without any JavaScript dependency.
You’ll need a simple HTML that shows/hides hovercards like so
1<html>
2 <style>
3 .has-hover-card {
4 display: inline-block;
5 position: relative;
6 }
7
8 .hover-card {
9 display: none;
10 background: #ddd;
11 border: 1px solid black;
12 padding: 10px;
13 position: absolute;
14 top: 1em;
15 left: 0;
16 white-space: nowrap;
17 }
18
19 .has-hover-card:hover .hover-card {
20 display: inline-block;
21 }
22 </style>
23 <body>
24 User:
25 <span class="has-hover-card">
26 Emad Elsaid
27 <span class="hover-card">
28 User: Emad Elsaid <br />
29 Title: Software Engineer <br />
30 Web: <a href="https://www.emadelsaid.com">emadelsaid.com</a>
31 </span>
32 </span>
33 Has created a new repository, and 30 other actions.
34 </body>
35</html>
You can save this on your disk and open it in the browser, putting your mouse over my name will show the card, and moving away will hide it, what make this behavior possible is the display
CSS property change when we hover on the name, checkout the Style
tag.
I’ll use Ruby and Sinatra here so install Sinatra
1$ gem install sinatra
Move your HTML page to views/index.erb
and write a server
file as follows
1#!/usr/bin/env ruby
2
3require 'sinatra'
4set :port, 3000
5
6get '/' do
7 erb :index
8end
Make it executable
1$ chmod +x server
And run it
1$ ./server
It should listen on port 3000 so opening localhost:3000
will show your page.
But as your page grows inserting the card for every name on the page will hurt your performance, so to make this page faster we’ll load only the card when the user move on the name.
Remove your hover card from the body and add a reference in the parent to the URL that will return it from the server.
1User:
2<span data-hover-card="/card/emad elsaid" class="has-hover-card">Emad Elsaid</span>
3Has created a new repository, and 30 other actions.
4
5<script type="text/javascript" src="/hovercard.js"> </script>
The endpoint can take the user name as a parameter as follows
1get '/card/:name' do
2 erb :card, locals: { name: params[:name] }
3end
And the card views/card.erb
can print anything the server pass to it like that:
1<span class="hover-card">
2 User: <%= name %> <br />
3 Title: Software Engineer <br />
4 Web: <a href="https://www.emadelsaid.com">emadelsaid.com</a>
5</span>
Now if you visit localhost:3000/card/emad elsaid
it’ll return the hover card content.
We now need to load the card when hovering on any element with data-hover-card
attribute, we’ll load it once in the page lifetime then append it to this element.
your hovercard.js
will look like that
1function loadCard(event) {
2 let target = event.target;
3 target.removeEventListener('mouseover', loadCard);
4 let url = target.getAttribute('data-hover-card');
5
6 fetch(url).then(function(response) {
7 return response.text();
8 }).then(function(html){
9 target.insertAdjacentHTML('beforeend', html);
10 });
11}
12
13let hasHoverCards = document.querySelectorAll('[data-hover-card]');
14hasHoverCards.forEach(function(element) {
15 element.addEventListener('mouseover', loadCard);
16});
It’ll attach a function to the mouseover
of every element that has data-hover-card
attribute, the function will remove itself to avoid loading the card twice.
Then we’ll get the card from the server and append it to this element.
This approach doesn’t need Sinatra/ruby at all, any web server will work, it doesn’t depend on any JavaScript package, or other dependencies other than the browser itself and CSS to show/hide the card.
You can generalize this technique to other things you want to fetch from the server on click or any other event.