The issue arises from the same issue as last time. My websites run off a static domain, so I want to be able to use this script on each site without making duplicate copies.
Here is an approach using promises for sleeping between key presses.
Here is a link for the repo at Github, but the code is basically this:
class Typer {
constructor(typingSpeed, content, output) {
this.typingSpeed = typingSpeed;
// Parses a NodeList to a series of chained promises
this.parseHtml(Array.from(content), output);
};
makePromise(node, output) {
if (node.nodeType == 1) // element
{
// When a new html tag is detected, append it to the document
return new Promise((resolve) => {
var tag = $(node.outerHTML.replace(node.innerHTML, ""));
tag.appendTo(output);
resolve(tag);
});
} else if (node.nodeType == 3) // text
{
// When text is detected, create a promise that appends a character
// and sleeps for a while before adding the next one, and so on...
return this.type(node, output, 0);
} else {
console.warn("Unknown node type");
}
}
parseHtml(nodes, output) {
return nodes.reduce((previous, current) => previous
.then(() => this.makePromise(current, output)
.then((output) => this.parseHtml(Array.from(current.childNodes), output))), Promise.resolve());
}
type(node, output, textPosition) {
var textIncrement = textPosition + 1;
var substring = node.data.substring(textPosition, textIncrement);
if (substring !== "") {
return new Promise(resolve => setTimeout(resolve, this.typingSpeed))
.then(() => output.append(substring))
.then(() => this.type(node, output, textIncrement));
}
return Promise.resolve(output);
}
}
If you want to define what text it prints out, you should pass the text through an argument, if I understand your question correctly.
Try and mess with this:
var type = function( elem , text , index )
{
var index = index||0;
elem.innerHTML += text.charAt(index);
index++;
var t = setTimeout(function(){
type( elem , text , index );
},100);
}
type( document.getElementById('screen') , 'How\'re You?' );
<p id="screen">Hello, </p>
2 Years Later:
Check out this awesome Typing & erasing effect plus a blinking cursor - CodePen
In a Nutshell:
var index = 0;
var text = "The Typing Effect - In a Nutshell";
function type(){
var screenEl = $('#screen');
screenEl.html(text.substr(0, index++));
if (index < text.length) {
// Feel free to type
setTimeout('type()', 50);
} else {
// Reset and restart.
index = 0;
text = '';
}
};
type();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id="screen"></p>
Nice question, LMGTFY has often given me a giggle in the past. I think you may find the following to be pretty easy to throw around anywhere. It's just a few attributes added to your target container, along with a call to get the typewriter started.
Here, I run 4 of them simultaneously just for kicks. It's probably worth junking forEachNode in this example, instead using the few commented lines. If the result of getElementsByClassName was a true array, you could just call the .forEach method that arrays have. Unfortunately, a nodeList is similar but not the same - hence the need for such a function. I used it before realizing it probably clearer to do without it. In any case, it's a function I've found handy many times. I'll leave that in there as a thanks for such a fun question to consider.
function forEachNode(nodeList, func) {
var i, n = nodeList.length;
for (i = 0; i < n; i++) {
func(nodeList[i], i, nodeList);
}
}
window.addEventListener('load', mInit, false);
function typeWriter(el) {
var myDelay = el.getAttribute('keyDelay');
if (el.getAttribute('curIndex') == undefined)
el.setAttribute('curIndex', 0);
var curIndex = el.getAttribute('curIndex');
var curStr = el.getAttribute('typewriterdata');
el.innerHTML += curStr.charAt(curIndex);
curIndex++;
el.setAttribute('curIndex', curIndex);
if (curIndex < curStr.length)
setTimeout(callback, myDelay);
else {
if (el.getAttribute('nextline') != undefined) {
var nextTgt = el.getAttribute('nextline');
typeWriter(document.getElementById(nextTgt));
}
}
function callback() {
typeWriter(el);
}
}
function mInit() {
typeWriter(document.getElementById('line1'));
var i, n, elementList;
elementList = document.getElementsByClassName('autoType');
forEachNode(elementList, typeWriter);
// n = elementList.length;
// for (i=0; i<n; i++)
// typeWriter( elementList[i] );
}
.multi {
border: solid 2px #333333;
width: 400px;
}
<body>
<div class='autoType' typewriterdata='Enter this string letter by letter' keydelay='300'></div>
<div class='autoType' typewriterdata='Enter this string letter by letter' keydelay='200'></div>
<div class='autoType' typewriterdata='This is short but slooooow' keydelay='1000'></div>
<div class='autoType' typewriterdata='The rain falls mainly on the plain in Spain' keydelay='100'></div>
<div class='multi'>
<div id='line1' typewriterdata='This is line 1' keydelay='300' nextline='line2'></div>
<div id='line2' typewriterdata='This is line 2' keydelay='300' nextline='line3'></div>
<div id='line3' typewriterdata='This is line 3' keydelay='300' nextline='line4'></div>
<div id='line4' typewriterdata='This is line 4' keydelay='300'></div>
</div>
</body>
Okay, I don't like any of the above code. Your original code also doesn't stop running once it reaches the end of the input text, and I don't believe any of the other suggested solutions stop either.
Here's a rewritten function in pure JS:
function type(i, t, ie, oe) {
input = document.getElementById(ie).innerHTML;
document.getElementById(oe).innerHTML += input.charAt(i);
setTimeout(function(){
((i < input.length - 1) ? type(i+1, t, ie, oe) : false);
}, t);
}
Which you can call like so:
type(0, 100, "text", "screen");
The parameters are: beginning index
, speed
, input element
, output element
Your HTML will look something like this:
<div id="screen"></div>
<div id="text" style="display:none">Hello Bobby</div>
You can rename the divs to whatever you like, as long as you update the parameters accordingly. I'm sure there's an easier way to write this as well, but I like this method the most.
function type(i, t, ie, oe) {
input = document.getElementById(ie).innerHTML;
document.getElementById(oe).innerHTML += input.charAt(i);
setTimeout(function(){
((i < input.length - 1) ? type(i+1, t, ie, oe) : false);
}, t);
}
type(0, 100, "text", "screen");
<div id="screen"></div>
<div id="text" style="display:none">Hello Bobby</div>
You can embed the text in the webpage itself in a hidden element like this:
HTML
<span id="hiddenText" style="display: none">Text you want to type out.</span>
and then you can get the text from the webpage itself like this:
Javascript
var text = document.getElementById('hiddenText').innerHTML;
Here is the jsfiddle you can see: http://jsfiddle.net/FMq6d/ . This makes minimal changes to your code.