This is one of those things that seem really simple on paper, but are weirdly complex to actually implement.
Good ol' marquee
Let's start with the simplest possible marquee
tag.
<marquee>Hello, World</marquee>
We can try repeating the content -
<marquee>
Hello, World
Hello, World
</marquee>
Let's fix marquee
This is where things get hairy. marquee by default, doesn't have a way to make text wrapping continuous. We must re-implement the marquee tag in some custom code.
Basic markup-
<style>
#outer {
border: 2px solid red;
}
#outer div {
display: inline-block;
}
#loop {
white-space: nowrap;
}
</style>
<div id="outer">
<!-- This div is important! It lets us specify margin-left as percentage later on. -->
<div>
<div id="loop"><div id="content">Hello, World </div></div>
</div>
</div>
First, we repeat the innerHTML
of the content till it "fills up" at least the width of the outer
container. Doing this in JS, as opposed to manually writing the text a bunch of times makes this code responsive
to all screen sizes.
let outer = document.querySelector("#outer")
let content = document.querySelector("#content");
repeatContent(content, outer.offsetWidth);
repeatContent
takes any DOM element and repeats it's innerHTML
till it reaches a
desired
cut-off
function repeatContent(el, till) {
let html = el.innerHTML;
let counter= 0; // prevents infinite loop
while (el.offsetWidth < till && counter < 100) {
el.innerHTML += html;
counter += 1;
}
}
Next, we add some animation using CSS
#loop {
animation: loop-anim 5s linear infinite;
}
@keyframes loop-anim {
0% {
margin-left: 0;
}
100% {
margin-left: -50% /* This works because of the div between "outer" and "loop" */
}
}
We also need to repeat the content itself, twice. so everything looks continuous, and there are no empty spaces in the animation. JS comes in handy here -
let el = outer.querySelector('#loop');
el.innerHTML = el.innerHTML + el.innerHTML;
Add some colours to visualize what's actually going on here -
<style>
#loop div:nth-child(1) {
background: rgba(154, 255, 102, 0.2);
}
#loop div:nth-child(2) {
background: rgba(123, 235, 255, 0.2);
}
</style>
Here's what it looks like without any of the styling
Perfect, right?! This is the same technique used on this blog to show the beautiful moving ASCII art :). Full code can be found in this Stack Overflow answer.