问题
Hi I'm trying to recreate what http://austintexas.wayblazer.com/locations/austin-tx is doing for its search textbox - displaying typed character and rotating between several phrases.
My initial logic is to read the phrases from an array like so: [abc,123,XYZ] and then to use a split and forEach to separate the string into characters and create a typing effect using setTimeout.
However, when I attempt this logic, I seem to have trouble getting the desired effect. I've been reading through a lot of these questions on SO, but few of them address issues with nested for loops.
Desired results:
- a b c (in a typing effect character by character)
- clear textbox
- 1 2 3 (in a typing effect character by character)
- clear textbox
- X Y Z
Actual results:
- abc123XYZ (abc shows up together at once, followed by 123, then XYZ)
$(document).ready(function() {
$('input#123').attr("placeholder", "");
var phrases = ['abc', '123', 'XYZ'];
for (var i = 0; i < phrases.length; i++) {
$('input#123').attr("placeholder", "");
(function(ind) {
var sentence = phrases[ind];
sentence.split("").forEach(function(char, index) {
setTimeout(function() {
$('input#123').attr("placeholder", $('input#123').attr("placeholder") + char);
}, ind * 500);
});
})(i);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<input id="123" />
Is there an elegant solution for what I want to achieve, and hopefully some explanation to enlighten me would be very much appreciated!
回答1:
The great thing with javascript is closures, so you can create function closures, and push onto an array to act like a stack.. You can then pop these function off inside a setInterval..
$(document).ready(function(){
var input = $('input#123'),
phrases = ['Stack Overflow','Is the place you come','To get help with coding stuff'],
typedelay = 140,
waitops = 5,
cmds = [],
ph = input.attr.bind(input, 'placeholder');
function clear() {
ph('');
}
function addLetter(a) {
return function () {
ph(ph() + a);
}
}
function doLoop() {
cmds = [];
for (var i=0; i<phrases.length;i++) {
cmds.push(clear);
var sentence = phrases[i];
sentence.split("").forEach(function(char,index) {
cmds.push(addLetter(char));
});
//at the end of each sentence, there is a pause. lets do some no ops,.. 5*300 ms
for (var nn = 0; nn < waitops; nn ++) cmds.push(0);
}
//lets make this thing go on forever.. :)
cmds.push(doLoop);
}
doLoop();
var icheck = setInterval(function () {
//is there a cmd waiting, pop it and run it.
var cmd = cmds.shift();
if (cmd) { cmd(); }
}, typedelay);
input.focus(function () {
clearInterval(icheck);
cmds = [];
ph('What your want');
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<input id="123" />
回答2:
Here's a recursive function that shifts the array and stops when array has no length
typePhrases(['Message #1','Another message','And the last one'], 300)
function typePhrases(phrases, speed){
// shift array to get word/phrase
var word = phrases.length && phrases.shift(), $input= $('#test').attr('placeholder','');
// don't do anything if no phrase/word
if(word){
word.split('').forEach(function(char, i){
setTimeout(function(){
// use attr(attributeName, function) to update
$input.attr('placeholder', function(_, curr){
return curr + char;
});
// on last letter call function again to get next word
if(i=== word.length-1){
setTimeout(function(){
typePhrases(phrases, speed);
},speed);
}
},i*speed)
});
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="test" placeholder=''>
来源:https://stackoverflow.com/questions/39885261/javascript-settimeout-closure-inside-nested-double-for-loops