JavaScript code execution delay - javascript

function append(what) {
$("#drawer").append(what);
}
function outerHtml(o) {
return $("<div />").append($(o).clone()).html();
}
var allPixel = [];
$(".pix").each(function() {
allPixel.push(outerHtml($(this)));
});
$("#drawer").empty();
var index;
for (index = 0; index < allPixel.length; index++) {
pixel = allPixel[index];
setTimeout(append(pixel), 100);
}
I have a Container (#drawer) that is full of many div elements. In this function the HTML of each of these div elements gets saved in an array individually. When its done doing that the div element gets cleared.
In the Array allPixel are now all div elements. I want it so that each div element gets added to #drawer with a delay of 100ms.
Problem:
If I run this function nothing happens (the divs are not disappearing/ appearing). What did I do wrong?
If anything is unclear feel free to let me know and I will edit my question.
Question aswered... See it in action: https://wiese2.lima-city.de/test/

You are invoking the method invoking immediately and passing its return value i.e. undefined to setTimeout.
You can set additional parameters to be passed to append function when timer expires to setTimeout method.
Use
setTimeout(append, 100 * (i + 1), pixel);
Additionally you also need to increase timer based on element index

Related

queryselector only happen once on text element

I'm struggling understanding some basics concept I guess,
I have this JavaScript function which take a dom element and should increase it's number every time the button is clicked, the thing is, the number increase once and never increase again, console.log show the actual number but not the stPage element. From my understanding it has to deal with the DOM element stored, so I have to kill the first child, I think I did so, but it doesn't seems to work.
If some can help me, please do, thank you really much <3
function stpage(){
//Setting child element + simple increment function
let numberNode = document.getElementById('stPage');
let articleNumber = 0;
articleNumber++;
//killing the child then display the element on the page
numberNode.removeChild(numberNode.childNodes[0]);
document.querySelector('#stPage').innerHTML = articleNumber;
};
Edit: articleNumber has to be outside the function scope.
let articleNumber = 0;
function stpage(){
articleNumber++;
document.querySelector('#stPage').innerHTML = articleNumber;
};
You are re-initializing articleNumber to 0 every time the function is called on this line:
let articleNumber = 0;
It should be something like:
let articleNumber = 0; // outside function scope, doesn't reset
function stpage() {
let numberNode = document.getElementById('stPage');
articleNumber++;
numberNode.innerHTML = articleNumber;
};

Replace contents of <div> in a loop while waiting

I'm having difficulty figuring out my issue, or even finding solutions for it, which leads me to believe I may be going in the wrong direction.
On a cshtml page I have an ajax function tied to a button click. That routes back to my controller and a json string array is returned back to the client.
On the page itself I have <pre id="replaceMe">***</pre> defined. I am attempting to iterate through the json array doing $("#replaceMe").replaceWith('<pre id="replaceMe">' + data[i] + '</pre>');
Technically speaking this works, but only in the sense that the last update is visible. I might as well just go straight to the last element of the array.
I've tried using setTimeout to no avail, no changes and then suddenly the last element is displayed. I've found some sleep like functions that mimic the same basic behavior, all with similar results. I did see some recommendations for an async sleep, but none of my browsers seem to like the async and give me an error about a missing ;.
I then thought I could do something like
function updateSection(data) {
for (var i = 0; i < data.length; i++){
var section = $("#replaceMe");
section.fadeOut(500);
section.replaceWith('<pre id="replaceMe">' + data[i] + '</pre>');
section.fadeIn(500);
}
}
That however has the same end result. No apparent change and then suddenly it's the last element in the array.
I'm clearly going about this wrong, otherwise I'd find an example fairly readily I think, so what should I be doing instead?
To clarify and sum up, I want to replace the content of the <pre></pre> with text that's contained in an array. I want each iteration to be visible long enough for a human to see it and observe the changes (~1000ms) before going to the next iteration.
If, for example the array contains "Tom", "Dick", "Harry", then I would like for the page to have
<pre id="replaceMe">Tom</pre> for 1 second, then that element is replaced with
<pre id="replaceMe">Dick</pre> for 1 second, then that element is replaced with
<pre id="replaceMe">Harry</pre>
I am NOT looking for
<pre id="replaceMe">Tom</pre>
<pre id="replaceMe">Dick</pre>
<pre id="replaceMe">Harry</pre>
setTimeout in a for loop runs after the for loop execution completed. so, you always see the last value. to solve this, you can use $.each method which provides a callback function or use an Immediately Invoked Function Expression.
more detailed info: https://codehandbook.org/understanding-settimeout-inside-for-loop-in-javascript/
var data=[];
for(var i=0; i<10; i++){
data.push(i+' lorem ipsum doloret');
}
$.each(data, function(i, el){
setTimeout(function(){
$("#replaceMe").replaceWith('<pre id="replaceMe">' + data[i] + '</pre>');
},500 + ( i * 1000 ));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<pre id="replaceMe">***</pre>
You can do that using setInterval function:
var words = ['interating', 'and', 'replacing', 'text'];
var replace = document.querySelector('#replace');
var count = 0;
function replaceText() {
replace.innerHTML = words[count];
if(count === words.length - 1)
count = 0;
else
count++;
}
setInterval(replaceText, 1000);
Updating:
You don't need to replace all the element, you can replace only the content, using the atribute innerText.
//pass in the data to loop over, and the index to show
function updateSection(data, index) {
//get a reference to the section
var $section = $('#replaceMe');
//fade out the section over 500 milliseconds, then perform callback on finish
$section.fadeOut(500, () => {
//change the text
$section.text(data[index]);
//fade in the section over 500 milliseconds, and then advance the index
//use modulus to reset the index to 0 when it reaches beyond the array
$section.fadeIn(500, () => updateSection(data, ++index % data.length));
});
}
updateSection(['Tom', 'Dick', 'Harry'], 0);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="replaceMe">***</div>

Jquery selector to "this" return

I have the following code:
function checkBoxOpenStatus(clickedBox) {
if($("#productsBox").hasClass("boxOpen")) {
closeBox(obj.productsBox.bx, clickedBox);
delay = 200;
} else if($("#fabricationBox").hasClass("boxOpen")) {
closeBox(obj.fabricationBox.bx, clickedBox);
delay = 200;
} else if($("#aboutPBBox").hasClass("boxOpen")) {
closeBox(obj.aboutPBBox.bx, clickedBox);
delay = 200;
} else { delay = 0; }
My goal is to evaluate three boxes, if one of them has class "boxOpen", then I need to close that box, and add a delay to my "openSelectedBox" function (not present above). My question is, can I change the above code to the following: (without loosing the ability to send a specific selector?)
if($("#productsBox" || "#fabricationBox" || "#aboutPBBox").hasClass('boxOpen') {
closeBox(obj.(**WHAT SELECTOR CAN GO HERE??**).bx, clickedBox);
delay = 200;
I know the "or" evaluation works, but I don't know how to use that "or" defined instance to fire the "closeBox" function with the appropriate selection. I would rather learn that take, so please give me an explanation which selector could work there while performing an evaluation like this.
You noticed duplicated code blocks and wanted to optimize them. (Insert I like you meme here).
You can do it this way:
// This will get the 3 elements in a jQuery object and filter the 3 elements to only keep the ones that have the class "boxOpen"
var theOnesWithTheClass = $("#productsBox, #fabricationBox, #aboutPBBox").filter(".boxOpen");
// This will call the closeBox with the id of each element inside the jQuery object. I do it this way just in case there are more than 1 element.
// The jQuery each method will call the function setting the this context to each element inside the jQuery object.
theOnesWithTheClass.each(function() {
closeBox(obj[this.id].bx, clickedBox);
});
// This will set the delay to 200 only if there are elements inside the jQuery object. The ? conditional returns the block before the : if true and the block after it if false.
delay = theOnesWithTheClass.length > 0 ? 200 : 0;
Use a selector that matches all the boxes with the class. If this returns at least 1 element, there's an open box, and you can get this element's ID.
Then you can use another selector to get the ID of the element with the class.
var openBoxes = $("#productBox.boxOpen, #fabricationBox.boxOpen, #aboutPBBox.boxOpen");
if (openBoxes.length > 0) {
var openId = openBoxes.attr("id");
closeBox(obj[openId].bx, clickedBox);
delay = 200;
} else {
delay = 0;
}
It would be a little simpler if you gave all these boxes a common class, instead of having to list all the IDs in the selector.

how to set anchor tag individual item to an array images

I added dynamic images display in specific div but I could not set link (a href)for each image individually.Could you help me?
Here is my script which I used but not working:
<script>
var i;
var timerid = 0;
var images = ["img1.jpg",
"img2.jpg",
"img3.jpg","img4.jpg"];
var countimages = 0;
function startTime()
{
if(timerid)
{
timerid = 0;
}
var tDate = new Date();
if(countimages == images.length)
{
countimages = 0;
}
if(tDate.getSeconds() % 4 == 0)
{
document.getElementById("img1").src = images[countimages];
}
countimages++;
timerid = setTimeout("startTime()", 800);
switch(countimages){
case images[0]: images[0].setAttribute('href',"dhow-cruise-creek-dubai.html");
images[0].innerHTML="dhow-cruise-creek-dubai.html";
document.appendChild(images[0]);
break;
case images[1]: images[1].setAttribute('href',"dhow-cruise-creek-dubai.html");
images[1].innerHTML="dhow-cruise-creek-dubai.html";
document.appendChild(images[1]);
break;
case images[2]: images[2].setAttribute('href',"dhow-cruise-creek-dubai.html");
images[2].innerHTML="dhow-cruise-creek-dubai.html";
document.appendChild(images[2]);
}
}
</script>
Several things here:
1: Your function is incrementing an array index (countimages) and wrapping it when it reaches the end of the index range of the array it is intended to subscript (images). You currently have two lines of code to accomplish this task, which are separated by another line of code. The two lines are
countimages++;
which is executed immediately after the subscripting of the aforementioned array, and
if (countimages == images.length) countimages = 0;
which is executed just before.
It would be much better, both for human readability and for code simplicity, to locate these two operations at the same spot in the code, because together they represent a single isolated and inseparable action. Also, the length cap can be applied more idiomatically and concisely using a modulus operation. The end result is you should delete the second line I showed above, and change the first to this:
countimages = (countimages+1)%images.length;
2: You are incrementing countimages in every evaluation of the function. This is happening even when the modulus test fails, and therefore the image is not changed. I suspect this is a mistake. Therefore I would change
if (tDate.getSeconds()%4 == 0) {
document.getElementById("img1").src = images[countimages];
}
countimages = (countimages+1)%images.length;
to
if (tDate.getSeconds()%4 == 0) {
document.getElementById("img1").src = images[countimages];
countimages = (countimages+1)%images.length;
}
3: I don't see any point in zeroing the timerid variable at the start of the function. It will inevitably be overwritten by the return value of the setTimeout() call later in the function. So the statement
if (timerid) timerid = 0;
should be removed.
4: The setTimeout() function supports two overloads. The first argument to the function can be either a function reference or a string of code. The former is preferable, both for performance and security reasons. So you should change
timerid = setTimeout('startTime()',800);
to
timerid = setTimeout(startTime,800);
But see below.
5: The setInterval() function is preferable to setTimeout() for a continuously repeating function call. Under this design, the function does not even need to reference timerid, or concern itself with its own invocation. We can just call setInterval() once during page load to start the chain of calls.
6: The switch statement at the end of the function is switching on countimages, which is of numeric type, against various elements of the images array specified by literal index, i.e. images[0], images[1], and images[2]. The images array holds string values representing image URLs, not numbers. So obviously this switch statement is incorrect. Also, the final element (images[3]) is omitted, which may be a mistake. If your intention was to switch on the indexes of the array, your case values should be 0, 1, etc. But see below.
7: Each of the case branches in the switch statement is identical to the others, except for the literal index. That is, they all follow this pattern, where i is the literal index:
case images[i]:
images[i].setAttribute('href','dhow-cruise-creek-dubai.html');
images[i].innerHTML = 'dhow-cruise-creek-dubai.html';
document.appendChild(images[i]);
break;
except that the final break; statement is missing from the final case branch.
This is an example of duplicate code that should be simplified by proper parameterization; in this case, parameterizing on i. Observe that the literal index always corresponds to the current value of countimages, so that is our i. In other words, the entire switch statement can be replaced with the following, again, assuming you wanted to switch on the indexes of the array:
images[countimages].setAttribute('href','dhow-cruise-creek-dubai.html');
images[countimages].innerHTML = 'dhow-cruise-creek-dubai.html';
document.appendChild(images[countimages]);
But see below.
8: The above lines of code are incorrect because they appear to be treating images as an array of elements, when it is in fact an array of strings. You cannot call setAttribute() on a string, nor is there a meaningful innerHTML property of strings, and you cannot append strings to the DOM tree using appendChild() (because strings do not implement the interface Node).
This brings us to your question. Your code seems to be trying to append a new anchor link element at the bottom of the entire document every time the image is advanced, but I doubt that's what you really want. I'm guessing you want to advance a single fixed anchor link element to a new href attribute and innerHTML content corresponding to the new image. To do this, I would recommend you change the array of strings to an array of hashes and store the href and innerHTML alongside the image URL using three key/value pairs.
9: The design of advancing the image and link during every multiple of 4 seconds, but checking for such a condition every 800 milliseconds, is very questionable. In some cases the check will be true twice in a multiple-of-4 second, in some cases it will be true only once during the multiple-of-4 second. And the moments the function is executed will drift, since the interval duration is not guaranteed to be exact. This would lead to some strange behavior. I suppose you may want this behavior, but I'm doubtful. Instead, I suspect what you're going for is for the image and link to advance once every 4 seconds. You can achieve this by removing the entire time test and just setting the interval to 4 seconds, that is, 4000 milliseconds.
Hence, we have:
var imageSpecs = [
{imageURL:'https://www.gravatar.com/avatar/8062178f34c7107a67ef00b681921287?s=328&d=identicon&r=PG&f=1',linkRef:'#1',innerHTML:'one' },
{imageURL:'https://www.gravatar.com/avatar/b57bf879dbb25c837c2e2ae730cab2cc?s=328&d=identicon&r=PG&f=1',linkRef:'#2',innerHTML:'two' },
{imageURL:'https://www.gravatar.com/avatar/166ed38dafa219c53980bb06cfce40b6?s=328&d=identicon&r=PG&f=1',linkRef:'#3',innerHTML:'three'},
{imageURL:'https://www.gravatar.com/avatar/0c8ea1549ebeff7bab9a282c4b965fa4?s=328&d=identicon&r=PG', linkRef:'#4',innerHTML:'four' },
];
// preload all images
for (let imageSpec in imageSpecs) {
let img = new Image();
img.src = imageSpec.imageURL;
} // end for
var nextImageSpecIndex = 0;
function advanceImageSpec() {
let imageSpec = imageSpecs[nextImageSpecIndex];
let imgElem = document.getElementById('img1');
imgElem.setAttribute('src',imageSpec.imageURL);
let linkElem = document.getElementById('link1');
linkElem.setAttribute('href',imageSpec.linkRef);
linkElem.innerHTML = imageSpec.innerHTML;
nextImageSpecIndex = (nextImageSpecIndex+1)%imageSpecs.length;
} // end advanceImageSpec()
var timerId = setInterval(advanceImageSpec,4000);
advanceImageSpec(); // immediate call to display first image immediately
#img1 { width:100px; height:100px; }
<div>
<img id="img1"/><br/>
<a id="link1"></a>
</div>
I'm not really sure what you're trying to do, but I assume you want the image to be a link that goes somewhere. Try putting a link tag around the img element with id img1 and giving that link tag the id `link1' like so:
<img id="img1"/>
Then change the script to this:
<script>
var i;
var timerid = 0;
var images = ["img1.jpg",
"img2.jpg",
"img3.jpg"];
var links = ["dhow-cruise-creek-dubai.html",
"dhow-cruise-creek-dubai.html",
"dhow-cruise-creek-dubai.html"];
var countimages = 0;
function startTime()
{
if(timerid)
{
timerid = 0;
}
var tDate = new Date();
if(countimages == images.length)
{
countimages = 0;
}
if(tDate.getSeconds() % 4 == 0)
{
document.getElementById("link1").href = links[countimages];
document.getElementById("img1").src = images[countimages];
}
countimages++;
timerid = setTimeout("startTime()", 800);
}
</script>
There's more that could be improved, but I'm sure you get where I'm going.

How to avoid reference to last index in a for loop while sending index as parameter for a function?

I have written this code:
function(targetSliderObject, images){
// create thumbs container
var thumbsContainer = $("<div></div>").addClass(defaultOptions.thumbsContainerClass);
// add thumbs
for(i in images)
{
thumb = $("<img />").addClass(defaultOptions.thumbItemClass).addClass(i);
thumb.attr("src", images[i]);
thumb.click(function(){
methods.slideNext(targetSliderObject, i);
});
thumb.appendTo(thumbsContainer);
}
// add thumbs container to container
targetSliderObject.append(thumbsContainer);
}
What I intend to do is calling method.slideNext() with a different number each time, but insted I get to send a reference to i as parameter for the function which, in the end, is the last index of my array for all my thumbs.
What trick can I use to accomplish what I want ?
Thank you!
"Anchor" the value of i:
(function(i) {
// code that uses i
})(i);

Categories