Progressively enhancing a link into a button with vanilla JavaScript
In yesterday’s article on loading HTML from another page, I talked about progressively enhancing a link into a button.
Let’s imagine you have a dialog modal that loads some HTML. You might keep all that HTML in a separate file, and by default display a link to it.
<a href="/terms">Read the Terms of Service</a>
When your JS loads, you can progressively enhance it into a modal toggle.
let toggle = document.querySelector('[href="/terms"]');
toggle.setAttribute('role', 'button');
toggle.addEventListener('click', handleModal);
<!-- After the JS loads -->
<a role="button" href="/terms">Read the Terms of Service</a>
Buttons and links do different things. They have different semantic connotations, and different interaction patterns with the keyboard.
My click
event will also detect enter/return key presses, but not spacebar presses like a button normally would. For that, I would need to add a keydown
event listener, and filter out keys that weren’t the spacebar.
The [role="button"]
attribute tells screen readers “this is a button, not a link,” but doesn’t make the link behave exactly like a button.
As a few folks on Mastodon pointed out, an easier and more resilient approach might be to replace the link with a button entirely.
To do that, I would use the document.createElement()
method to create a button
element. Then I’d copy the textContent
and className
properties from the original link over to it.
let link = document.querySelector('[href="/terms"]');
let btn = document.createElement('button');
btn.textContent = link.textContent;
btn.className = link.className;
There are a lot of ways you can add and remove elements in the DOM, but for what we’re trying to do, the Element.replaceWith()
method is the easiest and most straightforward.
let link = document.querySelector('[href="/terms"]');
let btn = document.createElement('button');
btn.textContent = link.textContent;
btn.className = link.className;
link.replaceWith(btn);
Now, I can use just my click
event listener, and automatically detect spacebar presses, enter/return key presses, clicks, and taps.