Making Pixel Point’s Medium Widget Accessible
A review of accessibility and JavaScript DOM manipulation
The Problem(s)
Like many folks, as I started out writing articles on Medium I wanted a way to embed a “feed” on my personal portfolio website without having to manually add articles each time I posted them. There are a lot of resources out there covering this topic so I won’t repeat detailed instructions here.
I eventually settled on using Pixel Point’s free unofficial medium-widget which after you to enter your Medium profile URL and configure a few options and gives you a block of HTML you can add to your site which does some JavaScript magic to create a nice Medium story feed. You can then further use some CSS to customize the look and feel. This works great, however, I found when testing my site for accessibility using the Web Accessibility Evaluation Tool (WAVE) that there were no alt
attributes on the <img>
elements that were generated by the widget’s script. If this is gibberish to you, read up on the HTML <img> alt Attribute.
The Process
Since the <img>
tags were being added to the DOM by the widget’s JavaScript on page load, it wasn’t as simple as just adding the attributes in my static HTML. I had to dust off my JavaScript in the DOM skills, which was a bit scary after living with the joy of React.js for so long. However, I figured this would be a great way to re-refresh my knowledge on the DOM and gain further appreciatation for what modern frameworks do for you.
Here is how I broke the problem down:
- Finding a source for the Alternative text
- Selecting the proper HTML elements from the DOM
- Adding the
alt
attribute
Finding a source for the Alternative text
Since the widget provides a “feed” of a defined number of articles, hard-coding the alt text for each images was not an option. That would also defeat the original purpose of using the widget so I didn’t have to update my site with each Medium post.
For the larger featured image I decided to use the description of the article that the widget pulls in, since this would be unique to the post and available in the DOM. This is a bit of a compromise, as ideally the alt
text would specifically describe the image but that wasn’t available to me on this page. For the authors avatar it was pretty clear I could grab the text from the name display to the right.
Selecting the HTML elements from the DOM
I initially thought this would be pretty straightforward given the widget’s use of fairly unique classes for each of the HTML elements. I used dev tools to determine the class names that I wanted to target. For example the anchor <a>
element containing the main<img>
element has a class of medium-widget-article__image
.
A unique class name meant it was as easy as document.getElementsByClassName("medium-widget-article__image")
and I’d have that element selected to manipulate right?
Wrong. Due to the asynchronous nature of the medium widget’s script, when my custom JavaScript was executing, the <a>
and <img>
tags were not actually in the DOM yet and I was getting an HTMLCollection of length 0 (so nothing)!
// First attempt at getting the <a> element
((d, w) => {
console.log(d.getElementsByClassName("medium-widget-article__image"));
})(document, window);
So I did what any good developer does and turned to google/stackoverflow to help me out. Throught a stackoverflow question I found the MutationObserver interface which allows you to watch for changes in the DOM then do your work. Here is an example of a simple use to find the <a>
elements with the medium-widget-article__image
class.
As you can see I’m now receiving an HTMLCollection with a length of three, corresponding to the three articles in the feed.
Adding the alt
attribute
Once I had identified the elements and was able to get and store them using the MutationObserver, now came the time to manipulate them.
I’ve added the finished code below with verbose commenting. I’ll explain a few of the additions here as well.
Main Image
In mainImgObserver
starting on line 4
I’m waiting until an element with medium-widget-article_image
is rendered, then getting that element and storing it in a variable on lines 8/9
. Note that I’ve turned the result from getElementsByClassName()
into an Array, and mapped over it to get the firstElementChild
. This gives me the first child element of the element I’ve selected. The reason for this is that the link <a>
element is the one with the class name specified, and the <img>
child element is the one I want to target.
I performed a similar operation to get the description text on lines 11/12
, however I didn’t need to get the child element here as I was able to directly select the element with the description given it had the specific class name.
Finally to apply the alt=
attributes to the <img>
elements I looped over the array of <img>
elements and set img.alt
equal to the corresponding description.
Avatar
The avatar ended up being much easier, as I learned from the WAVE tool that since the author’s avatar image was adjacent to the author’s name, I just needed to set the element like so <img alt="" ...
I realize this also applies to the Main Image as well, but for the sake of this article & learning/manipulating the DOM I’ve left adding the description in that alt tag.
HTML after the addition of alt tags
<!-- Main Image -->
<img src="https://cdn-images-1.medium.com/fit/300/300/1*DeuT8iD3FaiRTevzz4VODQ.png" alt="Reviewing how performance and accessibility requirements were met and thinking about the future"><!-- Avatar Image -->
<img src="https://miro.medium.com/fit/c/200/200/0*RgY7ABMwMVBvipXA" class="medium-widget-article__avatar-picture" alt="">
Conclusion
I now have a fully accessible Medium story feed on my personal portfolio website and have refreshed my knowledge on DOM manipulation.
If you’ve read this far, thanks for reading and I hope you’ve learned something as well. Let me know if you have feedback or would have done this in a different way!
Code
Code snippets: index.html, scripts.js
Check out my other projects and articles: