I don't know why the newly generated elements can't be seen by next called function? Thanks for help!!
Solution: Add async: false to disable asynchronous feature to make sure test-output-2 and test-output-3 executed after birth process. By default, ajax uses async: true that is sth like multi-threading.
function birth(mom)
{
$.ajax(
{url: "/cgi-bin/count.cgi", // return 3 for sure
async: false, // add this to disable asynchronous feature to make sure test-output-2 and test-output-3 executed after birth process
success: function(xkids) // xkids is 3
{
for( var i = 0; i < xkids; i++ )
{
mom.appendChild(document.createElement("div"));
mom.children[i].setAttribute("id", "child-"+i);
}
document.getElementById("test-output-1").innerHTML = mom.children.length; // now there are 3 children
}
});
document.getElementById("test-output-2").innerHTML = mom.children.length; // there are 0 children if async: true
}
var marry = document.getElementById("Marry"); // currently no child
birth(marry);
function whereIsTheChildren()
{
document.getElementById("test-output-3").innerHTML = marry.children.length; // there are 0 children if async: true
}
whereIsTheChildren();
Trying to locate an element in the DOM before it's loaded won't work (the script runs as soon as its encountered. If this is above the html in the file, the element wont exist yet and thus, wont be found)
Similarly, firing off an AJAX request and then acting as though this was a synchronous operation (waits for the operation to finish before executing more code) will not work.
In the first instance, the code is encountered before the browser has had time to parse the HTML, thus the element doesn't exist in the DOM when you try to get a reference to it - this can be fixed by waiting for the document to signal that it's completed loading.
The second issue is that immediately after firing the birth function, the whereIsTheChildren function is fired. Unfortunately, the ajax request is still pending and so we've not got the results back from it yet that we need to use. This is fixed by putting the call to whereIsTheChildren inside the success call-back for the ajax request.
I've whipped up a quick example, using vanilla JS and PHP - just substitute the request to the php file with the one for your CGI.
getKidCount.php
<?php
echo "3";
?>
index.html
<!doctype html>
<html>
<head>
<script>
"use strict";
function byId(id,parent){return (parent == undefined ? document : parent).getElementById(id);}
function myAjaxGet(url, successCallback, errorCallback)
{
var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function()
{
if (this.readyState==4 && this.status==200)
successCallback(this);
}
ajax.onerror = function()
{
console.log("AJAX request failed to: " + url);
errorCallback(this);
}
ajax.open("GET", url, true);
ajax.send();
}
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
//birth(3, byId("Marry") );
myBirth( byId('Marry') );
}
function myBirth(parentElem)
{
myAjaxGet('getKidCount.php', onAjaxSuccess, onAjaxFail);
function onAjaxSuccess(ajax)
{
var numKids = parseInt(ajax.responseText);
for (var i=0; i<numKids; i++)
{
var div = document.createElement('div');
div.id = ("child-"+i);
parentElem.appendChild(div);
}
document.getElementById("test-output-1").innerHTML = parentElem.children.length; // now there are 3 children
whereIsTheChildren();
}
function onAjaxFail(ajax)
{
alert("Ajax failed. :(");
}
}
function whereIsTheChildren()
{
document.getElementById("test-output-2").innerHTML = byId('Marry').children.length; // there are 0 children
}
/*
function birth(xkids, mom)
{
for( var i = 0; i < xkids; i++ )
{
mom.appendChild(document.createElement("div"));
mom.children[i].setAttribute("id", "child-"+i);
}
document.getElementById("test-output-1").innerHTML = mom.children.length; // now there are 3 children
}
function birth(mom)
{
$.ajax(
{url: "/cgi-bin/count.cgi", // return 3 for sure
success: function(xkids) // xkids is 3
{
for( var i = 0; i < xkids; i++ )
{
mom.appendChild(document.createElement("div"));
mom.children[i].setAttribute("id", "child-"+i);
}
document.getElementById("test-output-1").innerHTML = mom.children.length; // now there are 3 children
}
document.getElementById("test-output-2").innerHTML = mom.children.length; // now there are 0 children
}
*/
</script>
</head>
<body>
<div id='test-output-1'></div>
<div id='test-output-2'></div>
<div id='Marry'></div>
</body>
</html>
Modified to represent in DOM as well is in console.log
function birth(xkids, mom) {
var mom = document.querySelector(mom);
console.log('Mom: '+mom.id);
for (var i = 0; i < xkids; i++) {
mom.appendChild(document.createElement("div"));
mom.children[i].setAttribute("id", "child-" + i);
mom.children[i].innerHTML = mom.children[i].id;
}
console.log(mom.id+' has '+mom.children.length+' children');
var test = document.createElement("output");
document.body.appendChild(test);
test.value = mom.id + ' ' + mom.children.length;
}
birth(3, '#Marry');
birth(5, '#Liz');
birth(2, '#Betty');
div {
outline: 1px solid black;
width: 100px;
height: 30px;
}
output {
outline: 1px solid red;
color: red;
margin: 10px auto;
padding: 2px;
float: left;
}
.mom {
outline: 1px dashed blue;
width: 100px;
height: auto;
padding: 5px;
display: inline-block;
}
<div id="Marry" class="mom">Marry</div>
<div id="Liz" class="mom">Liz</div>
<div id="Betty" class="mom">Betty</div>
did you put this in window.onload event handler? your code is working, check this fiddle
window.onload=function(){
function birth(xkids, mom)
{
for( var i = 0; i < xkids; i++ )
{
mom.appendChild(document.createElement("div"));
mom.children[i].setAttribute("id", "child-"+i);
}
document.getElementById("test-output-1").innerHTML = mom.children.length; // now there are 3 children
}
var marry = document.getElementById("Marry"); // currently no child
birth(3, marry);
function whereIsTheChildren()
{
document.getElementById("test-output-2").innerHTML = marry.children.length; // there are 0 children
}
whereIsTheChildren();
}
Related
I found the similar question about using alert() but changing that to confirm() doesn't work. Need to return the response. This results in the "Not confirmed" alert being displayed first. What do I need to do to fix this?
var originalConfirm = window.confirm;
window.confirm = function(args) {
document.querySelector("html").classList.add("darkenPage");
setTimeout(function() {
var x = originalConfirm(args);
document.querySelector("html").classList.remove("darkenPage");
return (x);
});
}
html.darkenPage {
background-color: black;
}
html.darkenPage body {
opacity: 0.6;
}
<button onclick="if (confirm('Hello World, yes or no')) { alert('Confirmed'); } else { alert('Not confirmed'); } ">Confirm</button>
setTimeout runs the callback asynchronously, that means the return value inside the callback is not actually used, and the confirm function will return before the timeout callback is called. You'd need to use another callback as argument to confirm in order to get the response, but that would change the original API.
var originalConfirm = window.confirm;
window.confirm = function(args, callback) {
document.querySelector("html").classList.add("darkenPage");
setTimeout(function() {
var x = originalConfirm(args);
document.querySelector("html").classList.remove("darkenPage");
callback(x);
});
}
html.darkenPage {
background-color: black;
}
html.darkenPage body {
opacity: 0.6;
}
<button onclick="confirm('Hello World, yes or no', function (confirmed) { alert(confirmed ? 'Confirmed' : 'Not confirmed'); })">Confirm</button>
I have created a snippet of code that changes the state from display:block to display:none by using an onClick element. My goal is to delay the change in state, for a few seconds whilst an animation effect occurs.
This snippet of code below is what I am currently using to change the state of the element, but unsure on how to incorporate a delay.
Any advice or suggestions is greatly appreciated.
Thanks,
Ant
function showDiv1(elem) {
var divsToCheck = ["close","Holder"]; // Add to here to check more divs
for (var i = 0; i < divsToCheck.length; i++) {
if (divsToCheck[i] == elem) {
document.getElementById(divsToCheck[i]).style.display = "block";
} else {
document.getElementById(divsToCheck[i]).style.display = "none";
}
}
}
Put the code you want to be delayed inside an anonymous function, like so:
function showDiv1(elem) {
var divsToCheck = ["close","Holder"]; // Add to here to check more divs
for (var i = 0; i < divsToCheck.length; i++) {
if (divsToCheck[i] == elem) {
setTimeout(function() {
document.getElementById(divsToCheck[i]).style.display = "block";
}, 500);
} else {
setTimeout(function() {
document.getElementById(divsToCheck[i]).style.display = "none";
}, 500);
}
}
}
Here, 500 means delaying by 500 ms. You can change this to whatever amount of time (in milliseconds) that you need.
You should call a function in your loop that takes care of the setTimeout and hiding/showing instead of calling the timeout function in a loop. (Won't work because i is no longer available). See here: https://jsbin.com/refodobaca/1/edit?html,css,js,output
function showDiv1(elem) {
var divsToCheck = ["close","Holder"]; // Add to here to check more divs
for (var i = 0; i < divsToCheck.length; i++) {
if (divsToCheck[i] == elem) {
showHideDiv(divsToCheck[i], true);
} else {
showHideDiv(divsToCheck[i], false);
}
}
}
function showHideDiv(elem, bShow) {
var timeoutSeconds = 3;
setTimeout(function() {
document.getElementById(elem).style.display = (bShow ? "block" : "none");
}, timeoutSeconds * 1000);
}
showDiv1('Holder');
Your question is a little unclear because the name of your function is showDiv1 but you explain that you're trying to hide an element, so I've tried to answer in light of this and hopefully it will give you some ideas.
This code displays a couple of divs. If you click on them they turn red and, after a couple of seconds (to represent an animation), they are hidden.
dummyAnim returns a promise. After the animation has run its course (here represented by a two second delay) it is resolved. I've used await to pause code execution in an async function until the animation has resolved.
// Grab the elements and add click handlers to them
const divs = document.querySelectorAll('div');
divs.forEach(div => div.addEventListener('click', hideElement, false));
function dummyAnim() {
// return a promise which resolves (an animation)
// after two seconds
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), 2000);
});
}
async function hideElement() {
this.classList.add('red');
await dummyAnim();
this.classList.add('hidden');
}
div { color: black; display: block }
.hidden { display: none }
.red { color: red }
<div>close</div>
<div>holder</div>
I'm using this code to change the content of an div
mydiv.innerHTML = "html code with text and images";
Is there some events I can use to get a notification when everything is loaded? (no matter if there is no or many images )
I guess I could go through all child elements and addeventlisteners to them, but wish there is some other way.
Maybe something like this, just to give you an idea in vanilla javascript
var updateContent = function(element, content) {
element.innerHTML = content;
var images = element.querySelectorAll('img');
var loadedItems = 0;
var totalItems = images.length;
var loaded = function() {
loadedItems++;
console.log('loaded item: ' + loadedItems);
if(loadedItems == totalItems) {
element.classList.add('load-completed')
console.log('finished loading all images');
}
};
for(var i=0; i < totalItems; i++){
if(images[i].complete) {
loaded();
} else {
images[i].addEventListener('load', loaded);
images[i].addEventListener('error', function() {
console.log('error');
});
}
}
}
updateContent(
document.querySelector('.my-div'),
'<h1>hello world<\h1><img src="https://picsum.photos/id/237/200/300"><img src="https://picsum.photos/id/1/200/300">'
);
.my-div {
border:solid 5px orange;
}
.my-div img {
width: 300px;
height: 200px;
}
.load-completed {
border: dashed 5px green;
}
<div class="my-div"></div>
Edit 2012 live The live method is deprecated as of jQuery 1.7.0.
The .on() event is now recommended for attaching event handlers.
This replaces .bind(), .delegate(), and .live().
See the docs: http://api.jquery.com/on/
Original Answer
i think jQuery .live() event might be what you're looking for.
try this
mydiv.live('load', function () {
//code here
});
This question already has answers here:
Coordinating parallel execution in node.js
(7 answers)
Closed 5 years ago.
I am implementing the IIFE method when wrapping a for a loop around an async/ajax call.
var j = 4;
for (var i = 0; i < j; i++) {
(function(cntr) {
asyncCall(function() {
console.log(cntr);
});
})(i);
}
The problem is that when I console.log cntr, I get all of the values, but they have a random order. Let's say I have a for loop from 0-4. It will print these values in a random order, like 2,1,3,4,0. This changes every time I rerun the code.
Edit:
The question linked to most certainly is not the answer. Please pay more attention before marking as duplicate. I'm also not even using nodejs...
Your asyncCall doesn't finish in a constant amount of time. Since you begin each asynchronous call within a synchronous for-loop, whichever call finishes first will log first (in other words, the calls do not wait for each other).
There are two ways to go about fixing this problem. The first is to wait for each asyncCall to complete before beginning the next, which you could do easily with recursion:
var calls = 4
var i = 0
asyncCall(function handler() {
console.log(i)
if (++i < calls) asyncCall(handler)
})
function asyncCall(handler) {
setTimeout(handler, 0)
}
This approach makes the most sense if each successive call depends on the result of the previous call, which yours might not. In that case, it is more efficient to have each call execute up-front, but store the results in an array which you log once every call has completed:
var calls = 4
var done = 0
var i = 0
var results = []
for (var i = 0; i < calls; i++) (function(i) {
asyncCall(function handler() {
results[i] = i // example data
if (++done === calls) complete()
})
})(i)
function complete () {
// Do something with your array of results
results.map(function (e) {
console.log(e)
})
}
function asyncCall(handler) {
setTimeout(handler, Math.random()*10)
}
A self-executing closer, which it looks like you're looking for, is used like this:
//<![CDATA[
/* external.js */
var doc, bod, htm, C, T, E; // for use onload elsewhere
addEventListener('load', function(){
doc = document; bod = doc.body; htm = doc.documentElement;
C = function(tag){
return doc.createElement(tag);
}
T = function T(tag){
return doc.getElementsByTagName(tag);
}
E = function(id){
return doc.getElementById(id);
}
addClassName = function(element, className){
var rx = new RegExp('^(.+\s)*'+className+'(\s.+)*$');
if(!element.className.match(rx)){
element.className += ' '+className;
}
return element.className;
}
removeClassName = function(element, className){
element.className = element.className.replace(new RegExp('\s?'+className), '');
return element.className;
}
var outs = doc.getElementsByClassName('output');
for(var i=0,out,l=outs.length; i<l; i++){
(function(){
var out = outs[i], b = false;
out.onclick = function(){
if(b){
removeClassName(out, 'blue'); b = false;
}
else{
addClassName(out, 'blue'); b = true;
}
}
}());
}
}); // close load
//]]>
/* external.css */
html,body{
padding:0; margin:0;
}
.main{
width:980px; margin:0 auto;
}
.output{
width:100px; border:1px solid #000; border-top:0;
}
.output:first-child{
border-top:1px solid #000;
}
.blue{
background:lightblue;
}
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
<head>
<meta http-equiv='content-type' content='text/html;charset=utf-8' />
<link type='text/css' rel='stylesheet' href='external.css' />
<script type='text/javascript' src='external.js'></script>
</head>
<body>
<div class='main'>
<div class='output'>Sorry</div>
<div class='output'>This</div>
<div class='output'>Example</div>
<div class='output'>Isn't</div>
<div class='output'>Better</div>
</div>
</body>
</html>
Here's how the closure works. Events actually fire when they occur, like onclick. By the time the Event fires you are actually at the end of the loop, so when that happens, vars and arguments look for the level they were last scoped to.
I would just like to know if it is good practice to have a click event listener in a function like the example below. (Don't try to understand what the code does, I just wanted to show an example of a situation where a click event is in a function )
function someFn(classClickedBtn, popupId) {
$(classClickedBtn).click(function(e) {
e.preventDefault();
var active = "active";
var mainClass = ".dialogBox";
if ( $(popupId).hasClass(active) ) {
$(popupId).removeClass(active);
}else{
for (var i = 0; i< $(mainClass).length; i++) {
if ( $(mainClass).hasClass("active") ) {
$(mainClass).removeClass("active"); } }
$(popupId).addClass(active);
}
});
}
someFn(".btn1", "#popup");
someFn(".btn2", "#popup");
If your purpose is to attach an event handler for 'click' event of 'triggerBtnClass', only if something call your function before, so yes.
I'm okay with calling the listener from within your function. You can make things a little easier on yourself by calling the function one time with all arguments included. Have a look at the refactored code below. I'm using an object literal to store the function arguments and calling the function one time, passing the object as a single parameter.
function someFn(fn_data) {
$(fn_data.btns).on("click", function(e) {
var active = "active";
var mainClass = $(".dialogBox");
var popup = $(fn_data.popupId);
e.preventDefault();
if (popup.hasClass(active)) {
popup.removeClass(active);
} else {
for (var i = 0; i < mainClass.length; i++) {
if ( mainClass.hasClass("active") ) {
mainClass.removeClass("active");
}
}
popup.addClass(active);
}
});
}
someFn({
btns: ".btn1, .btn2",
popupId: "#popup"
});
#popup {
margin-top: 1em;
}
#popup.active:after {
content: 'Popup active!';
}
#popup:after {
content: 'Popup not active!';
color: #555;
display: block;
height: 20px;
white-space: nowrap;
margin-top: 10px;
border: 1px solid orange;
padding: 0.5em 1em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="btn1">button 1</button>
<button class="btn2">button 2</button>
<div id="popup">popup goes here</div>