How do I make jQuery statement execute after .load()? - javascript

Sorry for the possibly terrible question title. I don't know how to word it properly. Please offer suggestions.
Anyhoo, I have this html:
<ul class="image_reel">
<li class="thin"><img class="gal_thumb" src="8681.jpg"></li>
<li class=""><img class="gal_thumb" src="DSC_7586.jpg"></li>
<li class="thin"><img class="gal_thumb" src="DSC_7601.jpg"></li>
</ul>
I want to assign the li either a 'thick' or 'thin' class based on the height of the child img. So I have this jQuery:
// add 'thin' class to parent li
jQuery('.image_reel li a img').load(
function() {
var t = jQuery(this);
var h = this.naturalHeight;
if( h < 800 ) {
jQuery(t).parent().parent().addClass( 'thin' );
}
});
// now sort lis
var $images = jQuery('.image_reel');
var $imagesli = $images.children('li');
$imagesli.sort(function(a,b){
var aT = jQuery(a).hasClass('thin');
var bT = jQuery(b).hasClass('thin');
if( aT == true && ( bT == false ) )
return 1;
else
return 0;
});
$imagesli.detach().appendTo($images);
The problem seems to be that the first block seems to execute -after- the second block. Or maybe they execute synchronously? Regardless, the code doesn't work. So... how do I make the first block of code execute -before- the 2nd?
The weird thing is that, if I use the Firefox 'Q' debugger, the code actually 'works'. But without the debugger it doesn't. I assume that the debugger forces the code to run in some sort of special order.

Wrap your section block in a function, and then call it after the load function ends, like put it in the return
// add 'thin' class to parent li
jQuery('.image_reel li a img').load(function() {
var t = jQuery(this);
var h = this.naturalHeight;
if( h < 800 ) {
jQuery(t).parent().parent().addClass( 'thin' );
}
return loadBlock();
});
function loadBlock() {
// now sort lis
var $images = jQuery('.image_reel');
var $imagesli = $images.children('li');
$imagesli.sort(function(a,b){
var aT = jQuery(a).hasClass('thin');
var bT = jQuery(b).hasClass('thin');
if( aT == true && ( bT == false ) ) {
return 1;
} else {
return 0;
}
});
$imagesli.detach().appendTo($images);
}
Or use a package like async .waterfall or .series
You can use something like async as I mentioned above to have better flow control over async functions here's an example on how you could avoid calling on every callback
async.each($("img"), function(e, callback) {
$(this).load(function() {
console.log("bdbdbd");
callback(); // Done loading image
});
}, function() {
console.log("Done loading images");
loadBlock();
});
Async package can and will be your best friend if utilized properly.
I'm on mobile so I can't really test but this worked just fine on jsbin just throw your code in there and it should work.
this works because when you call jQuery('.image_reel li a img').load this will run its own loop on every element it finds and attach the event listener to it.
The method I used was I used async.each which takes an array, in this case I provided $('#img') as the array which will be a collection of any element it finds that matches the query, then async.each will run the loops in parallel so we don't have to wait for one to finish before the loop can proceed to the next thing.
Then in the loop body we call .load on .this which is attaching the .load function on only 1 element at a time and not trying to do its own internal loop on all the elements, so this way when the function completes we know that its done cause that function is only running on on element. Then we call callback(); which is required for async.each to let .each know that the function body is done and it can proceed, when all loops trigger their callback the loop ends and then the main function executes (the function that's the third argument to .each). You can see more about async.each here: async.each

The second block executes before the first block because the load() resolves immediately and only calls the first block later when it has finished. To do what you want, call the second block at the end of the first block.
// add 'thin' class to parent li
jQuery('.image_reel li a img').load(
function() {
var t = jQuery(this);
var h = this.naturalHeight;
if( h < 800 ) {
jQuery(t).parent().parent().addClass( 'thin' );
}
doWork();
});
function doWork() {
// now sort lis
var $images = jQuery('.image_reel');
var $imagesli = $images.children('li');
$imagesli.sort(function(a,b){
var aT = jQuery(a).hasClass('thin');
var bT = jQuery(b).hasClass('thin');
if( aT == true && ( bT == false ) )
return 1;
else
return 0;
});
$imagesli.detach().appendTo($images);
}

Related

do something when js element is loaded , is not working

I have this Jquery function to click on an element when its ready. its an interval doing it , the following function:
MonitorAndClick(selector) {
var ele = $(selector);
if (ele.length == 0) {
var intervalid = setInterval(function () {
var ele = $(selector);
if (ele.length > 0) {
ele[0].click();
clearInterval(intervalid);
return true;
}
}, 500);
} else {
ele[0].click();
return true;
}
}
the problem is in some cases , its not working. however this is an interval , and it's checking the element to be ready every 0.5 sec, so how can it be possible ? is there any other way to check the element is ready ?
additional note:
I have an accordion. I have a function to open the accordion->open one of the items->open the tab page in detail section
this is the function :
//--reach to this point, open accordion index 2--------
ShowAccordion(2);
//----open the item with specific Id in accordion items------
setTimeout(function () {
var selector = "tr[gacategory = '/myprotection/mywills/item_" + parseInt(willId) + "]";
MonitorAndClick(selector);
}, 500);
the point is this element SHOULD be there , sometimes its not loading fast enough , and I WANT TO HAVE A WAY TO CHECK IF ITS LOADED, THEN CLICK ON THAT.
Updated code after comments
var selector = "tr[gacategory = '/myprotection/mywills/item_" + parseInt(willId) + "]";
$("#selector").ready(function () {
console.log('**********.... selector is loaded ....*****');
if (!$("#selector").hasClass('selected'))
MonitorAndClick(selector);
});
still not working.
Why do you want to rely on 0.5 seconds delay to make sure your element is present in DOM. You should be invoking this function only after your element is present in the DOM. If there is another condition that drives when this element is added to the DOM, then call this function once that condition is achieved.
You may want to try https://api.jquery.com/ready/
It seems like jquery ready function can be applied on individual elements too

Jquery .remove() does not update DOM

I am trying to remove an element form the DOM using the .remove() jQuery method
Basically i am parsing a list and removing certain elements. Then right after, i reparse the list for some treatment for the rest of the elements.
But a simple printout of the size of the list gives me the impression that the elements to be filtered out were not removed
$list = $(".elements_list");
alert( $list.size());
$list.each(function(){
if ( $(this).data("quantity") == 0)
{
$(this).slideUp(1000,function(){
$(this).remove();
});
}
});
change_background_colors();
Right after this treatment, i call another function that has the following code in the beginning:
function change_background_colors() {
$list = $(".elements_list");
alert($list.size());
...
}
I get the same size of the list before and after removing elements...
Is there something wrong in my approach ?
Thanks!
if you call the size alert in a setTimeOut function you will see
$list = $(".elements_list");
alert($list.size());
$list.each(function () {
if ($(this).data("quantity") == 0) {
$(this).slideUp(1000, function () {
$(this).remove();
});
}
});
setTimeout(function () {
$list = $(".elements_list");
alert($list.size());
}, 2000);
JSFiddle:
https://jsfiddle.net/upkkLq2m/2/
The element is not removed until after 1000 milliseconds pass and the animation completes. Wait until then to count the elements.
edit: here's the more complicated way to delay till all animates complete:
$list = $(".elements_list");
var n = 0;
$list.each(function(){
if ( $(this).data("quantity") == 0) {
n = n + 1; // one more callback to wait for
$(this).slideUp(1000,function(){
$(this).remove();
n = n-1;
checkIfAllDone();
});
}
});
function checkIfAllDone(){
if(n===0){ // be sure that n was declared in the same scope as this function
change_background_colors();
}
}

synchronise code in javascript, wait for event to complete

I've got a problem near that one.
Somewhere I've got a binding over a file input :
var file;
function initLoadImg(){
$('#test').on('change', function() {
file = event.target.files;
// block 1
console.log("hello");
center.html('<span id="Tamb">25°C</span>');
over = true;
});
}
And i'm triggering it with another javascript function :
var over = false;
var center;
function loadImg(){
var elem = $('<div class="widget simpleimgchart center"><div class="matable"><div class="center"></div></div></div>');
center = elem.children().children();
$("#test").trigger('click');
passIfOver();
// block 2
console.log("bye");
return elem;
}
function passIfOver() {
if (over) {
return;
} else {
setTimeout(passIfOver(), 1000);
}
}
This way, I'm able to see the "hello" before the "bye" in the console.
However I don't really like this solution, (it's not clean) and user can have to wait up to 1s before getting any feedback.
Would there be another way to ensure that the return elem is executed after the end of the callback on click?
edit : My code doesn't even work, because of the setTimeout, I lose the restraint...
edit 2 : My goal is to execute the part code 1 before the part code 2. I don't want my function loadImg() to return before the code 1 has finished to execute.
I recommend you to look at PubSub pattern (http://davidwalsh.name/pubsub-javascript).
Just move the return inside the Trigger function:
var over = false;
function loadImg(){
var elem = $('<div class="widget simpleimgchart center"><div class="matable"><div class="center"></div></div></div>');
var center = elem.children().children();
$("#test").trigger('click', function(){
center.html('<span id="Tamb">25°C</span>');
return elem;
});
}
The second argument to .trigger is a callback function everything inside it will be executed After the trigger is completed.

How to initiate an action after 3 mouse clicks

I am new to writing code but I'm trying to figure out to have a div disappear after being clicked three times. I know how to get it to disappear after one or two clicks but I'm not sure how to do it after three. I wrote a while loop that should iterate up once after each click but instead the function doesn't wait for the div to be clicked and goes ahead and fades out the div.
var threeClick = function() {
var n = 0;
while(n > 2) {
$(document).ready(function() {
$('div').click(function(){
n++;
});
});
$(document).ready(function(){
$('div').fadeOut('slow');
});
}
}
threeClick();
var n
$(document).ready(function()
{
n=0;
$('div').click(function()
{
n++;
if(n==3)
{
n=0;
$('div').fadeOut('slow');
}
});
}
$(document).ready(function() {
var n = 0;
$('div').click(function() {
n++;
if (n == 3) {
$(this).fadeOut('slow');
}
});
});
see this
You don't have to repeat $(document).ready. This is a method (from jQuery) called when DOM is ready. So your code should go in this function;
This should work:
$(document).ready(function() {
var n = 0;
$('div').click(function() {
n++;
if(n == 3) {
$('div').fadeOut('slow');
}
});
});
I'm wondering why your while loop doesn't block execution while it sits and spins. JavaScript is not multi-threaded; there is a single thread of execution and I would imagine that the while would block that thread. But aside from that it won't really work because you're never checking the value of n before you fade out the div. This is why the fade-out happens almost immediately. There is also no need for numerous $(document).ready(...) calls; one will do.
I would also recommend using .on:
$(document).ready(function() {
var n = 0;
$('div').on('click', function() {
n++;
if(n >= 3) {
$('div').fadeOut('slow');
}
});
});
This works because the n which has been defined in the anonymous function (passed to .ready) is available to the callback (closure) passed to .on or .click. Closures are lexically bound to the scope in which they are defined, which means that anything defined in the enclosed scope is available to the closure. So your n's value will be updated and available to the closure.
You may try this too
$(function(){
$('#myDiv').on('click', function(){
var clicks = $(this).data('clicks') || 0;
$(this).data('clicks', clicks+1);
if($(this).data('clicks') >=3 ) $(this).fadeOut();
});
});
DEMO.
You need to create the variable holding the count outside of the function or it will reset each time the function is called. Give the div a class name - here i have used 'divClassName'.
var numClicks = 0;
$(function() {
$(document).on("click",".divClassName",function() {
numClicks+=1;
if (numClicks > 2) {
$('div').fadeOut('slow');
}
}
};
With jQuery you could do something like that
var counter=0;
$('div').on('click',function(){
counter++;
if(counter==3)$('div').fadeOut();}
);

How to fire a callback function after a for-loop is done in Jquery?

I have two functions that caluclate and adjust the height and width of elements on screen.
panelHeight sets the height of target elements to the available screen height, while panelWidth adjusts the width of elements.
My problem:
I cannot ensure the second functions (panelWidth) fires AFTER the first function (panelHeight) is done. If the target element is long and has a scrollbar, it will be removed by panelHeight but if this is not done before panelWidth fires, the set width will be off by the space taken up by the scrollbar (17px - still present when the width is calculated).
So I'm looking for a way to fire a function only after another function is done. Sort of like a callback, but I'm not sure who to fiddle this in below for-loop:
panelHeight: function (from) {
var self = this,
o = self.options,
wrap = $('div:jqmData(wrapper="true").ui-page-active').last(),
overthrow = wrap.jqmData("scrollmode") == "overthrow" && $('html').hasClass('ui-splitview-mode'),
blacklist = $('html').hasClass('blacklist'),
// calculationg toolbars
// elements
contents = TARGET_ELEMENT;
if (overthrow) {
for ( var i = 0; i < contents.length; i++){
// calculate values
...
contents.eq(i).css({
"max-height": setH,
"margin-top": blacklist == true ? glbH + lclH : 0,
"margin-bottom": blacklist == true ? glbF + lclF : 0
})
}
} else {
for ( var i = 0; i < contents.length; i++){
// calculate values
...
contents.eq(i).css({
"max-height" : "",
"height": o._iPadFixHeight,
"margin-top": blacklist == true ?
parseFloat( lclH.outerHeight() ) : 0,
"margin-bottom": blacklist == true ?
parseFloat( lclF.outerHeight() ) : 0
})
}
}
// USING THIS NOW, WHICH IS NOT NICE
window.setTimeout(function(){ self.panelWidth(false ); },65)
},
So I'm either looping through the overthrow-if-or-else loop and only need to fire panelWidth *AFTER* the loop is finished.
Question:
Any idea how to get rid of the timeout and add the panelWidth function call to the end of the loop? I tried with queue, but this also requires a delay(xxx). Also I cannot fire panelWidth inside the for-loop. I need it to trigger once only once the height function is complete
EDIT:
Would this be possible:
// calling my panelHeight function like this:
self.panelHeight("source").done(function() {
// call panelWidth:
self.panelWidth();
});
If so, where would I have to put the var deferred = Deferred();?
SOLUTION:
Got it to work. Thanks all! Here is my solution:
When calling panelHeight, add done() handler
$(document).on('pagechange.fixedHeight', function() {
self.panelHeight("pagechange").done( function() {
self.panelWidth( false )
});
});
Inside panelHeight, declare defer and return resolved();
panelHeight: function (from) {
var deferred = $.Deferred();
... run function
return deferred.resolve();
}
This is the purpose of the JQuery Deferred...
var deferred = $.Deferred();
somethingAsync(function() {
// callback stuff here
// now tell the deferred task it's ok to proceed
deferred.resolve();
}
deferred.done(function() {
// your finalize code here
});
Edit
Since the resolve event needs to be tied to a dom resizing, maybe the Javascript resize event handler would work.

Categories