A better marquee that does what I want it to do.
In my opinion, the semi-standard HTML marquee element is pretty bad. Here are my main reasons:
1: Its behavior is seemingly random across different browsers.
2: It only really works with one element inside it and if you try for more...
3: The dead space before it can loop back around.
I though I had more than three reasons, but I guess not. BUT THAT SHOULD BE ENOUGH! The thought of a marquee is already bad enough because of its lack of regard for web accessibility, but to add on all of that is too much! So, I came up with a solution: ditch the marquee tag and do everything with CSS and JavaScript. I was hoping to get it all done with just CSS, but it's either impossible or a little bit too painful for me. Anyway, here's what I came up with.
HTML:
<div class="marquee">
<div class="marquee_content" aria-hidden="true">
<p>Marquee element #1</p>
<p>Marquee element #2</p>
<p>Marquee element #3</p>
</div>
</div>
CSS:
.marquee {
overflow: hidden;
&:hover .marquee_content {
animation-play-state: paused;
}
}
.marquee_content {
display:inline-block;
white-space:nowrap;
align-content:center;
animation:20s infinite linear marquee_scroll;
height:max-content;
min-width:max-content;
& {
display:inline-block;
text-align:left;
margin:0;
padding:0 var(--containerwidth) 0 0;
&:last-child {
padding:0;
}
}
}
.--vertical .marquee_content {
width:100%;
white-space:normal;
animation: 10s infinite linear marquee_vertical reverse;
& {
display: block;
text-align:center;
padding: 0 0 var(--containerheight) 0;
margin:0;
&:last-child {
padding:0;
}
}
}
@keyframes marquee_scroll {
0% {
transform: translate(var(--containerwidth), 0px);
}
100% {
transform: translate(-100%, 0px);
}
}
@keyframes marquee_vertical {
0% {
transform: translate(0px, -100%);
}
100% {
transform: translate(0px, var(--containerheight));
}
}
JavaScript:
document.addEventListener("readystatechange", _init_marquee);
function _init_marquee() {
let marquees = document.querySelectorAll(".marquee");
marquees.forEach((elem) => {
const parentWidth = elem.parentElement.offsetWidth;
elem.style.setProperty("--containerwidth", parentWidth + "px");
elem.style.setProperty("--containerheight", elem.offsetHeight + "px");
});
}
Let me explain a little bit for those that aren't yet so experienced.
HTML:
We create a div with the class "marquee". Inside of it we place another div with the class "marquee_content". This inside div is what will be scrolling. We give it the attribute aria-hidden so that screen readers ignore it because the marquee is mainly just for flavor and doesn't provide much information or context. Inside the this div we can put whatever we want to scroll. Also, the top level div with the "marquee" class, can also be given a "--vertical" class to make it scroll vertically. Just make sure to set the height of the marquee div.
CSS:
A lot is happening here, but one of the most important things we do is set the spacing between each element of the marquee. We do this using a style variable "--containerwidth" that is calculated for each marquee and set using JavaScript.
JavaScript:
We need to use JavaScript to set the --containerwidth variable because elements are not directly able to see the width of their parent or grandparent. The most important part is that we set an event listener on the document so that we only set the --containerwidth variable after it has loaded. This is necessary because your browser only knows the dimensions of each element after it has loaded and started rendering styles.
Issues:
The proper dimensions for the scrolling element is determined once the page has finished loading, so under normal circumstances it should work perfectly. However, if the window is resized and the marquee is not set to a constant width/height, then it will not adapt its speed or dimensions. This will result in slower/faster scrolling and skipping near the end of the marquee. This could be fixed by adding an event listener on window resize, but that could potentially cause quite a bit of lag depending on how many marquees you have and how often you resize your window. Because of that I have chosen to omit dynamic updating, but if you so desire, it should not be very difficult to implement it.
Feel free to use part or all of this on your web pages. If you do end up using all of it, please give me some credit or a backlink to my home page. I've been using this code for a while now. It has served me well and I hope it will help you too!
Thanks so much for reading through the end of this post. I know a lot of people don't really care how things work and just want to use a finished product, so it means a lot to me that you were willing to sit down, read all of this and hopefully learn something. I know my code isn't perfect, but I made it on my own and I'm proud of it and I think that's all that matters.
Also, this post was written in an updated version of my cms that supports limited markdown.