Delay show line of text, clear on complete and start again

I have a function below that shows a couple of sentences one line at a time with a fade, but the client has asked that once they all the sentences are shown that after a few seconds the lines clear only to start again from the top and keep doing that in a show, hide, show, hide loop but I dont know how to do the hide then show again.

<div id="greeting" style="color:white; margin-top:14px;">
<p style="color:white">‘Health is our heritage, our right.<br/>It is the complete and full union between soul, mind and body;<br/>and this is not a difficult far-away ideal to attain,<br/>but one so easy and natural&#8230;<br/>that many of us have overlooked it.’<br/>Dr Edward Bach<br/></p></div>

<script>
var h1 = $('div#greeting p');
h1.hide().contents().each(function() {
var words;
if (this.nodeType === 3) {
    words = '<span> ' + this.data.split('<br/> ').join('<br/> </span><span> ') + ' </span>';
    $(this).replaceWith(words);
} else if (this.nodeType === 1) {
    this.innerHTML = '<span> ' + this.innerHTML.split(/\s+/).join(' </span><span> ') + ' </span>';
}
});

h1.find('span').hide().each(function() {
if( !$.trim(this.innerHTML) ) {
    $(this).remove();
}
});

h1.show().find('span').each(function(i) {
$(this).delay(3000 * i).fadeIn(3500);
});
</script>

One way you could do it would be like so:

function fadeInLine(line){
  $('<span />', {
    html: line.trim(),
    style: "opacity: 0"
  })
  .appendTo($greeting)
  .animate({opacity:1}, 500);
}

function rotateLines(interval){
  setTimeout(() => {
    if(i >= lines.length-1){
      i = 0;
      interval = 2000;
      setTimeout(()=>{ $greeting.empty(); }, interval)
    } else {
      fadeInLine(lines[i]);
      i++;
      interval = 650;
    }
    rotateLines(interval);
  }, interval);
}

const $greeting = $('div#container p');
const text = $greeting.html();
const lines = text.split("\n").filter((e) => e.replace(/\s+/, ""));
let i = 0;

$greeting.empty();
rotateLines();

I tidied up your original code somewhat. Hopefully that’s more readable.

Here’s a demo.

1 Like

Shouldn’t it be if(i >= lines.length) otherwise the last line doesn’t get shown :slight_smile:

1 Like

Yup. Good catch.

Just lucky :slight_smile:

Just for fun here’s a css only version.

That’s mind blowing!

Is there any way you can have it pause for longer once the final line has been revealed before it starts again?

Yes you can just set the keyframe to be completed at 80% and then the time between 80 - 100% is just doing nothing. You can also create a rubbing out effect at the end by stopping at around 97%.

e.g.


@keyframes fade{
	0% {transform:translateY(0) }
	80%{transform:translateY(100%)}
  97%{transform:translateY(100%)}
}

I’ve updated the demo with the above keyframe.

Man! That rocks.

Are there any accessibility concerns / browser support issues to be aware of in this method?

It should work in all modern browsers and ie11+ but there is an issue for older IE versions that don’t support transform as they will get the invisible div. You could use @supports and only hide the element for modern browsers (that will exclude IE11 but edge should be ok).

Demo

@supports (transform:translateY(0)) {

.fade:after{
	content:"";
	position:absolute;
	z-index:2;
	left:0;
	right:0;
	top:0px;
	bottom:0;
	background:#fff;
	animation:fade 8s linear infinite;
}
@keyframes fade{
	0% {transform:translateY(0) }
	80%{transform:translateY(100%)}
  97%{transform:translateY(100%)}
}

}

There is also an issue if you have gradient or image backgrounds as the routine is just shimming a white background over the text to rub it out. You can of course change the colour to match whatever solid background you are on but if you had an image background you wanted to preserve then it would not be possible to use that method.

The other difference from your example is that the text is merely hidden and not appended but in most cases you would probably want this behaviour otherwise the rest of the page content would jump every time a line of text was revealed.

1 Like

Hi there PaulOB,

< offtopic >

deathshadow an ex-member here, suggests using this method…

@media (min-width: 1px) { 
   /* CSS3 only stuff here */ 
 }

As our resident CSS guru, do your have any comments about it?

< /offtopic >

coothead

Ah ok, thanks. I think I’d still opt for the JS path, as this is more like behaviour, not presentation, but it still blows my mind what you can do with CSS nowadays.

Good point. I guess on my demo, you’d probably need to set a height on the containing element.

Hi coothead,

These days I tend to avoid using hacks for old IE versions as they are quickly becoming irrelevant and indeed most of my clients are happy with ie11+ support.

The above hack is useful in some cases but would fail in my demo because IE9 reads the @media rule ok but then fails on the @keyframes rule so the result is still an invisible div. Conditional comments would solve the problem fully as IE9 was the last version to understand CC’s and therefore the damaging code could be reset for IE9 and under.

Demo with CCs

The media hack could be useful in some situations but you would still need to know that the browser understands the css3 properties you are using as many will understand @media but then not understand some other common rules like keyframes that you are using.

The @supports rule will be the way to go once we forget about ie11 and under:)

3 Likes

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.