I'm trying to create a html heading that changes itself every 5 seconds, through an array of multiple items and a loop. I can get everything to work, except the text changing after every fifth second. Right now, it immediately goes from the first item in the array, to the last. I've tried using setTimeOut and setInterval, but no luck so far. I've also searched everywhere online.
Here's what i have so far, without the setTimeOut/setInterval part, because it didn't work:
var headingChange = {
heading: function() {
var headings = ['Hello', 'Hi', 'Ye'];
for (var i = 0; i < headings.length; i++) {
document.getElementById('heading').innerHTML = headings[i];
}
}
};
Here's a jsfiddle to make it easier (+ html).
https://jsfiddle.net/countermb/w9qwk6ch/
Hope someone can help me (am new to Javascript). Thanks in advance.
This Snippet might help you.
You may need to use setInterval to change the heading.
var headings = ['Hello', 'Hi', 'Ye'];
var i = 0;
var intervalId = setInterval(function() {
document.getElementById('heading').innerHTML = headings[i];
if (i == (headings.length - 1)) {
i = 0;
//you can even clear interval here to make heading stay as last one in array
//cleanInterval(intervalId);
} else {
i++;
}
}, 4000)
<h1 id="heading">Change this!</h1>
You can use setInterval to run your function every n milliseconds. Have also updated the snipped to just increment an index (or go back to 0) instead of looping through the array with a for loop.
var headingChange = {
currentHeader: 0,
headings: ['Hello', 'Hi', 'Ye'],
heading: function() {
document.getElementById('heading').innerHTML = this.headings[this.currentHeader];
if (this.currentHeader === this.headings.length - 1) {
this.currentHeader = 0;
} else {
this.currentHeader = this.currentHeader + 1;
}
}
};
setInterval(function(){
headingChange.heading();
}, 5000)
Related
So I have a weird problem (as I can do this using dummy code, but cannot make it work in my actual code) -
The concept is simple - I need a for loop that upon hitting its max "I" number reverts "I" to 0 again and creates a loop over and over -
DUMMY CODE:
for(i=0;i<10;i++){
console.log(i);
if(i === 10){
i = 0
}
}
Now for the longer code (sorry)
function reviewF(){
// add ID to each of the objects
reviews.forEach((e, i)=>{
e.id = i
})
// get the elements to be populated on page
var name = document.querySelector('p.name');
var date = document.querySelector('p.date');
var rating = document.querySelector('.rating_stars');
var review = document.querySelector('p.review_content_text');
// reverse the array - so the newest reviews are shown first (this is due to how the reviews where downloaded)
var reviewBack = reviews.slice(0).reverse();
// start the loop - go over each array - take its details and apply it to the elements
/**
* THIS IS WHAT I WOULD LIKE TO LOOP OVER FOREVER
*
* **/
for (let i = 0; i < reviewBack.length; i++) {
(function(index) {
setTimeout(function() {
// document.getElementById('reviews').classList.remove('slideOut')
name.classList.remove('slideOut')
date.classList.remove('slideOut')
rating.classList.remove('slideOut')
review.classList.remove('slideOut')
name.classList.add('slideIn')
date.classList.add('slideIn')
rating.classList.add('slideIn')
review.classList.add('slideIn')
name.innerHTML = reviewBack[i].aditional_info_name;
date.innerHTML = reviewBack[i].Date;
rating.innerHTML = '';
review.innerHTML = reviewBack[i].aditional_info_short_testimonial;
if(reviewBack[i].aditional_info_short_testimonial === 'none'){
reviewBack.innerHTML='';
}
var numberOfStars = reviewBack[i].aditional_info_rating;
for(i=0;i<numberOfStars;i++){
var star = document.createElement('p');
star.className="stars";
rating.appendChild(star);
}
setTimeout(function(){
// document.getElementById('reviews').classList.add('slideOut')
name.classList.add('slideOut')
date.classList.add('slideOut')
rating.classList.add('slideOut')
review.classList.add('slideOut')
},9600)
}, i * 10000)
})(i);
// should create a infinite loop
}
console.log('Loop A')
}
// both functions are running as they should but the time out function for the delay of the transition is not?
reviewF();
EDITS >>>>>>>>
Ok so I have found a hack and slash way to fix the issue - but its not dry code and not good code but it works.....
this might make the desiered effect easier to understand
reviewF(); // <<< this is the init function
// this init2 function for the reviews waits until the reviews have run then
// calls it again
setTimeout(function(){
reviewF();
}, reviews.length*1000)
// this version of the innit doubles the number of reviews and calls it after that amount of time
setTimeout(function(){
reviewF();
}, (reviews.length*2)*1000)
From trying a bunch of different methods to solve this issue something I noticed was when I placed a console.log('Finished') at the end of the function and called it twice in a row (trying to stack the functions running..... yes I know a horrid and blunt way to try and solve the issue but I had gotten to that point) - it called by console.log's while the function was still running (i.e. the set time out section had not finished) - could this have something to do with it.
My apologies for the rough code.
Any help here would be really great as my own attempts to solve this have fallen short and I believe I might have missed something in how the code runs?
Warm regards,
W
Why not simply nest this for loop inside a do/while?
var looping = True
do {
for(i=0;i<10;i++){
console.log(i);
}
if (someEndCondition) {
looping = False;
}
}
while (looping);
I would think that resetting your loop would be as simple as setting "i = 0" like in the dummy code. So try putting the following into your code at the end of the for loop:
if(i === 10){
i = 0;
}
This question already has answers here:
javascript interval
(3 answers)
Closed 4 years ago.
$(function() {
let testOne = 'test one.';
let testTwo = 'test two';
let messageBox = $('messagebox');
let a = ['test:', testOne,'test2:', testTwo];
let i = 1
setInterval(cool, 1000)
function cool() {
messageBox.text(a[1])
}
});
Hi there,
I am new to JS. I am looking to have testOne and testTwo (going to add a few more) display in timers across my screen. I was given help to get this far.
I am trying to, for example, have a word and its English definition appear on the screen in a time loop, rotating the words in a loop. (kind of like a live screen-saver)
What am I missing?
Thank you for your time, help, and effort.
You've got a good start.
As others have mentioned, unless you're using a custom HTML element (i.e. <messagebox>), use # at the beginning of your selector to indicate that "messagebox" is an ID. See jQuery's ID Selector.
$('#messagebox')
Alternatively, use a class and the class selector.
$('.messagebox')
The index of the array element to display is currently hard-coded to 1. We want to increment it upon each iteration so the text will change. But we only want to count up to the number of array elements and then go back to the first one and start over.
Below, I'm using JavaScript's increment and remainder operators to increment i while limiting it to the number of elements in a. Note that the "postfix" method "returns the value before incrementing", so i starts at zero.
a[i++ % a.length]
Working example:
$(function() {
let $messageBox = $('#messagebox');
let testOne = 'test one.';
let testTwo = 'test two.';
let a = ['test:', testOne, 'test2:', testTwo];
let i = 0;
function cool() {
$messageBox.text(a[i++ % a.length])
}
setInterval(cool, 1000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="messagebox"></div>
EDIT
I don't like letting i count up indefinitely. The math might get wonky after about 9 quadrillion loop iterations, which is how high JavaScript can safely count.
Safe in this context refers to the ability to represent integers exactly and to correctly compare them. For example, Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2 will evaluate to true, which is mathematically incorrect. -- developer.mozilla.org
console.log(Number.MAX_SAFE_INTEGER);
console.log(Number.MAX_SAFE_INTEGER + 1);
console.log(Number.MAX_SAFE_INTEGER + 2);
console.log(Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2);
So, here's what happens after about three million centuries:
$(function() {
let $messageBox = $('#messagebox');
let testOne = 'test one.';
let testTwo = 'test two.';
let a = ['test:', testOne, 'test2:', testTwo];
let i = 9007199254740990;
function cool() {
console.log(i);
$messageBox.text(a[i++ % a.length])
}
setInterval(cool, 1000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="messagebox"></div>
That's not good enough.
We need this thing running well past the end of time.
Let's keep it safe:
$(function() {
let $messageBox = $('#messagebox');
let testOne = 'test one.';
let testTwo = 'test two.';
let a = ['test:', testOne, 'test2:', testTwo];
let i = 0;
function cycleText() {
console.log(i);
$messageBox.text(a[i]);
i = ++i % a.length;
setTimeout(cycleText, 1000);
}
cycleText();
});
body {
font-size: 2em;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="messagebox"></div>
You can easily swap out the messages in your array and update an html element using your code. Instead of passing a hardcoded index in, just increment a number until it reaches the length of the array (n < array.length) and reset it to 0.
I personally would recommend making your messagebox element a div or something out of the box just for readability sake (so nobody comes in and gets confused where messagebox is coming from). However, if you have a specific use case for a custom html element, make sure you're doing it correctly.
https://jsfiddle.net/mswilson4040/oxbn8t14/2/
<messagebox>Initial Value...</messagebox> // custom HTML element called messagebox
$(function() {
let testOne = 'test one.';
let testTwo = 'test two';
let interval = -1;
let messageBox = $('messagebox');
let a = ['test:', testOne,'test2:', testTwo];
// let i = 1 <-- this isn't doing anything
setInterval(cool, 1000)
function cool() {
interval = interval < a.length ? interval += 1 : 0;
messageBox.text(a[interval])
}
});
I am trying to mix the initial string and randomly compare the string's elements with the right elements on the right indexes, and if true push them into a set, to reconstruct the initial string. Doing this I met the problem that while loop does nothing just crushng the browser. Help me out with this.
function checker() {
var text = document.getElementById("inp").value;
var a = [];
var i = 0;
while (a.length < text.length) {
var int = setInterval((function() {
var rnd = Math.floor(Math.random() * text.length);
if (text[rnd] === text[i]) {
a.push(text[rnd]);
clearInterval(int);
i++;
}
}), 100)
}
}
P.S. I need the setInterval() function because I need the process to happen in exactly the same periods of time.
So, you stumbled into the pitfall most people hit at some point when they get in touch with asynchronous programming.
You cannot "wait" for an timeout/interval to finish - trying to do so would not work or block the whole page/browser. Any code that should run after the delay needs to be called from the callback you passed to setInterval when it's "done".
In my answer its doing exactly what you want - creating exactly the same string by randomly mixing the initial, and also using setInterval. You didn't write where you want the result, so you have it written in the console and also in another input field with id output_string.
HTML:
<input id="input_string" value="some_text" />
<input id="output_string" value="" readonly="readonly" />
JavaScript:
function checker() {
var text = document.getElementById("input_string").value;
var result = '';
// split your input string to array
text = text.split('');
var int = setInterval((function() {
var rnd = Math.floor(Math.random() * text.length);
// add random character from input string (array) to the result
result += text[rnd];
// remove used element from the input array
text.splice(rnd, 1);
// if all characters were used
if (text.length === 0) {
clearInterval(int);
console.log(result);
document.getElementById("output_string").value = result;
}
}), 100);
}
checker();
DEMO
Honestly, I have no idea what you are trying to do here, but you seem to have lost track of how your code is operating exactly.
All your while loop does, is creating the interval, which is ran asynchronous from the loop itself.
In other words, the only way your while condition equates to false, is after multiple 100ms intervals have elapsed. 100 miliseconds is an eternity when comparing it to the speed of 1 loop iteration. We're looking at 1000s of iterations before your first setInterval even triggers, not something a browser can keep up with, let alone wait several of these intervals before you change a.length.
Try more like this:
function checker() {
var text = document.getElementById("inp").value;
var a = [];
var i = 0;
// start to do a check every 100ms.
var interv = setInterval(function() {
var rnd = Math.floor(Math.random() * text.length);
if (text[rnd] === text[i]) {
a.push(text[rnd]);
i++;
}
// at the end of each call, see if a is long enough yet
if(a.length > text.length){
clearInterval(interv); // if so, stop this interval from running
alert(a); // and do whatever you need to in the UI.
}
}, 100);
}
}
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.
It functions as a typing text effect, I want to be able to define the text it prints out from the webpage itself and not the script.
Javascript
var index = 0;
var text = 'Text';
function type()
{
document.getElementById('screen').innerHTML += text.charAt(index);
index += 1;
var t = setTimeout('type()',100);
}
I've tried fiddling with the code and using them same method as my previous post, but I can't seem to get it to work.
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.
Demo
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>
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>
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.
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>
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>
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);
}
}
let typeSpeed = 300;
let deleteSpeed = 200;
let wordDelay = 1000;
// utility function that returns a promise that resolves after t milliseconds
const delay = (t) => {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
//Change Current Job
const changeCurrentJob = async (wordsJson) => {
//Get Current Job
let currentJob = document.getElementById('wrap');
for (let wordFromJson of wordsJson) {
//Deleting
//Previous word letters count
let prevLetters = currentJob.innerHTML.split('');
//Loop letters with for of to remove them
for(let letterFromWordPrev of currentJob.innerHTML){
//Remove Last letter
prevLetters.pop();
//Join Letters Array
currentJob.innerHTML = prevLetters.join('');
await delay(deleteSpeed);
}
//Typing
for(let letterFromWord of wordFromJson){
currentJob.innerHTML = currentJob.innerHTML+letterFromWord;
//Type Speed
await delay(typeSpeed);
}
//After finishing word Wait
await delay(wordDelay);
}
//ReDO Typing - Declare Variables then Redo -
let words = document.getElementsByClassName('typewrite');
let wordsData = words[0];
let wordsJson2 = JSON.parse(wordsData.getAttribute('data-type'));
changeCurrentJob(wordsJson2);
}
// On window load Loop data-type And convert from json to txt and type
window.onload = function() {
let words = document.getElementsByClassName('typewrite');
let wordsData = words[0];
let wordsJson = JSON.parse(wordsData.getAttribute('data-type'));
setTimeout(changeCurrentJob,wordDelay,wordsJson);
};
<div class="typewrite" data-type='[ "Full Stack", "PHP", "JS" ]'>
<div class="styledWarp" id="wrap"></div>
</div>
I'm getting confused with what's happening here. The quiz works fine the first time. After the first play, though, I get all sorts of problems. I want to click the same button,"#start2", to start and also restart the quiz, ie clear the timer, put all variables back to 0 etc, and display the first question. As if the page had been refreshed, basically.
Instead, I'm getting faster ticking, the timer is incrementing on correct guess and so on. Horrible.
I've used modulo to measure how many times the "#start2" div is clicked. On first click, start timer. On second click - I want to reset the timer. Third click - start timer, and so on.
Any help is massively appreciated.
var n = 0;
var x = 0;
var p = 0;
var incTime;
function a(n) {
var x,y,z;
x = Math.floor((Math.random() * 3))
if(x == 0){y = 1; z = 2}else if(x == 1){y = 0; z = 2}else{y = 0; z = 1}
$("#question_holder2").text(questions[n].q);
$(".answer_holder2").eq(x).text(questions[n].a).data('answer', 'a');
$(".answer_holder2").eq(y).text(questions[n].b).data('answer', 'b');
$(".answer_holder2").eq(z).text(questions[n].c).data('answer', 'c');
}
$(document).ready(function() {
//timing element
function startTimer(x){
$("#start2").text(x);
}
$("#start2").click(function(){
var setTimer;
p++;
//if it's been clicked before
if(p%2 === 0){
clearInterval(setTimer);
$("#start2").text("Start");
n = 0;
x = 0;
a(n);
alert("okay");
}else if(p%2 !== 0){
//never been clicked before
a(n);
setTimer = setInterval(function(){startTimer(x=x+1)}, 1000);
$('.answer_holder2').click(function() {
//correct answer given
if ($(this).data('answer') === 'a') {
n++;
if (n < questions.length) {
a(n);
} else {
alert("End of quiz!");
clearInterval(setTimer);
$("#start2").text("You took " + x + " seconds, you answered " + n + " questions correctly, with - incorrect answers given.");
x = 0;
n = 0;
a(n);
}
}else{
//incorrect answer given
$(this).fadeTo(1000,0.4);
var timeString = $("#start2").text();
var incTime = (timeString * 1) + 5;
$("#start2").text(incTime);
startTimer(incTime);
x = incTime;
};
});
};
});
});
You have this:
$("#start2").click(function(){
var setTimer;
p++;
//if it's been clicked before
if(p%2 === 0){
clearInterval(setTimer);
//....
In this case, when you set to the clearInterval line, setTimer will always be 0, and not the id of a running timer. So this is not actually stopping any timer. If you don't stop the timer it will continue to run. So the function here:
setTimer = setInterval(function(){startTimer(x=x+1)}, 1000);
Will continue to run. So the next time you create a timer, you now have two timers updating x and it'll look like it's running faster.
Try this:
$(document).ready(function() {
var setTimer;
$("#start2").click(function(){
// the rest of your click handler code...
});
//timing element
function startTimer(x){
$("#start2").text(x);
}
}
Your setTimer variable needs to exist in a scope outside of your click handler. As you had it you were declaring a new variable every time so when you try and clear the timer, you are not actually clearing the timer.
Also: freakish's point about how you are reattaching the click handler is also a problem. You need to fix that too.
The answer is that bad things happen because of this:
$("#start2").click(function(){
// some code...
$('.answer_holder2').click(function() {
// some code...
});
});
When you click on #start2 new handler is attached to .answer_holder2. So after for example 3 clicks, .answer_holder2 has 3 handlers attached to it and when you click on it all 3 fire.
You're code is a bit complicated and I'm not going to give you a solution how to fix that. But I can give you a hint. Put inner .click outside of outer .click. You will have to change some code probably, but that has to be done.
EDIT What you could try ( as a fast fix, but not necessarly good ) is adding this:
$('.answer_holder2').off( "click" ).click(function() {
Additonally have a look at Matt's answer.