I'm doing an application with Phonegap and I'm using a self-built slide transition to change the pages.
It works like this:
Every page is a div with 100% height and width, so if I change the Page, I set the next div right to the currently active and slide both to the left side.
Now to the Problem: the sliding works fine, but it's executed before the content of the right div is completely loaded. So the right div slides in empty, and only after a few hundred miliseconds the content will appear.
I tried it with document.ready, but as I've read this event is only executed the first time the DOM is loaded.
Does anybody know how I can wait for the DOM to be completely rendered again after I've manipulated the DOM with Javascript?
In your case, you can pick one element in the content of the next div and keep checking it with $(...).length. If the value is > 0, the DOM is loaded and you can change the page.
You may want to try this function:
Function.prototype.deferUntil = function(condition, timeLimit) {
var ret = condition();
if (ret) {
this(ret);
return null;
}
var self = this, interval = null, time = ( + new Date());
interval = setInterval(function() {
ret = condition();
if (!ret) {
if (timeLimit && (new Date() - time) >= timeLimit) {
// Nothing
} else {
return;
}
}
interval && clearInterval(interval);
self(ret);
}, 20);
return interval;
};
Usage:
(function() {
console.log('Next page loaded');
}).deferUntil(function() {
return $('#nextDiv').length > 0;
}, 3000);
The above example will check the div that has id="nextDiv" in every 20ms (not longer than 3 seconds). When the div is loaded, it will show 'Next page loaded' in the console.
You can try on this fiddle
There is a DOMNodeInserted event that is supposed to work like document.ready but for individual DOM nodes. But it is deprecated and has lots of issues. StackOverflow users found a good alternative to it that works quite well in all mobile browsers: Alternative to DOMNodeInserted
Here is a function that will trigger a callback once all images matching a jquery selector have finished loading
Js Fiddle Sample
//css
input {width: 420px;}
//html
<div id="container"></div>
<input type="text" value="http://goo.gl/31Vs" id="img1">
<br><input type="text" value="http://wall.alafoto.com/wp-content/uploads/2010/11/Fractal-Art-Wallpapers-09.jpg" id="img2">
<br><input type="text" value="http://pepinemom.p.e.pic.centerblog.net/ssg8hv4s.jpg" id="img3">
<br><input type="button" value="Load images" name="loadImages" id="btn">
<div id="message"></div>
//javascript
//Call a function after matching images have finished loading
function imagesLoadedEvent(selector, callback) {
var This = this;
this.images = $(selector);
this.nrImagesToLoad = this.images.length;
this.nrImagesLoaded = 0;
//check if images have already been cached and loaded
$.each(this.images, function (key, img) {
if (img.complete) {
This.nrImagesLoaded++;
}
if (This.nrImagesToLoad == This.nrImagesLoaded) {
callback(This.images);
}
});
this.images.load(function (evt) {
This.nrImagesLoaded++;
if (This.nrImagesToLoad == This.nrImagesLoaded) {
callback(This.images);
}
});
}
$("#btn").click(function () {
var c = $("#container"), evt;
c.empty();
c.append("<img src='" + $("#img1").val() + "' width=94>");
c.append("<img src='" + $("#img2").val() + "' width=94>");
c.append("<img src='" + $("#img3").val() + "' width=94>");
evt = new imagesLoadedEvent("img", allImagesLoaded);
});
function allImagesLoaded(imgs) {
//this is called when all images are loaded
$("#message").text("All images loaded");
setTimeout(function() {$("#message").text("");}, 2000);
}
You could use jQuery ajax to load the content, and on success run a function with the slide.
$("#page1").load('page2.html', function() {
//do your custom animation here
});
Althoug I'm not completely sure how you're loading the content. Is it static (Already there but just not visible?) Or is it loaded with ajax?
EDIT: You could just do a small .delay() or setTimeout with a few millisec, and then animate the sliding.
I had a similar problem making a masonry site responsive. I use window.onload which waits for all elements to complete loading before initialising masonry.js. I also placed the window.onload inside .onchange function and it fired everytime the viewport resized.
I am sure applying similar principles will solve your problem.
try once
$(window).bind('load',function(){
//code
});
Maybe you can set an event on your div.
myDiv.onafterupdate = myHandler;
function myHandler() {
// Do here what you want to do with the updated Div.
}
Does this help you?
In jquery you could use $() just after your DOM manipulation code.
$(function(){
//code that needs to be executed when DOM is ready, after manipulation
});
$() calls a function that either registers a DOM-ready callback (if a function is passed to it) or returns elements from the DOM (if a selector string or element is passed to it)
You can find more here
difference between $ and $() in jQuery
http://api.jquery.com/ready/
Related
I've created a custom modal dialog that is added and removed from the screen when it is called. However, when I'm trying to remove it, the remove function doesn't seem to be working in certain circumstances.
This is the close function from the modal (triggered by clicking on the close button):
function modal_close() {
$('.custom_block_page').fadeOut().remove();
$(this).parent().fadeOut().remove();
};
This is how I call that function from the button inside the modal dialog:
MatchGame.closeWin = function() {
$('.custom_modal_close').trigger('click');
MatchGame.playGame();
};
If I just click the close button, the dialog is removed and everything works as expected. But when I trigger the close, the dialog fades to nothing, but remains in the body so it displays again the next time it is called.
Checking the console between I get:
$('.custom_block_page').length
1 // displayed the first time
$('.custom_block_page').length
0 // during the 2nd game (expected)
$('.custom_block_page').length
2 // displayed after the 2nd game; I expect this to be 1
I've tried putting a timeout on my playGame, but that didn't seem to help either.
Thanks for the help!
The issue you observe is due to .fadeOut(), which is implemented asynchronously in a whole series of later event threads.
Meanwhile, in the original event thread .remove(), subsequent statements, returning from the function and subsequent statements in the function's caller, ALL execute synchronously - well before .fadeOut() has completed.
The solution is to exploit .promise(), which will return a jQuery promise, from which you can chain .then() :
function modal_close() {
return $('.custom_block_page').add($(this).parent()).fadeOut().promise().then(function() {
$(this).remove();
});
};
In the caller, .trigger() returns jQuery but you now need to work with the returned promise, therefore use .triggerHandler().
MatchGame.closeWin = function() {
$('.custom_modal_close').triggerHandler('click').then(function() {
MatchGame.playGame();
});
};
Edit:
Code from add_block_page() and add_popup_box() can be safely rolled into show_modal_box() to make one larger function.
By doing so, you will benefit from being able to access the variables $block_page, $pop_up, $close, $inner from the close button's click handler.
function show_modal_box() {
var $block_page = $('<div class="custom_block_page"></div>').appendTo('body'); // dark background
var $pop_up = $('<div class="custom_modal_box"></div>').appendTo($block_page);
var $close = $('').appendTo($pop_up);
var $inner = $('<div class="custom_inner_modal_box">loading...</div>').appendTo($pop_up);
if(options.name != '') {
$pop_up.attr('id', options.name);
}
// Add the content - if url, load the page otherwise use the text
if (options.url != '') {
$inner.load(options.url);
} else {
var innerHTML = '';
if(options.title[0] === "<") { // assume formatting
innerHTML += options.title;
} else {
innerHTML += '<h2>' + options.title + '</h2>';
}
if(options.description[0] === "<") {
innerHTML += options.description;
} else {
innerHTML += '<p>' + options.description + '</p>';
}
$inner.html(innerHTML);
}
$close.click(function() {
// for example
return $pop_up.fadeOut().promise().then(function() {
$block_page.remove();
});
});
$(window).off('resize.popup').on('resize.popup', add_styles).trigger('resize.popup'); // prevent accumulation of resize handlers
// checkNeedScroll();
$pop_up.fadeIn();
}
EDIT 2
I think I have it!
In the custom_modal_box plugin, the code below causes a click handler to be appended to this:
return this.click(function(e) {
show_modal_box();
});
That's fine if the plugin is invoked just once on any particular element however in this game's code it is invoked on the same element, $('.win'), every time a game is completed.
To prevent an accumulation of click handlers on $('.win'), change that code to :
return this.off('click.popup').on('click.popup', function(e) {
show_modal_box();
});
I'm attempting to dynamically create an iFrame and add HTML into it from the current document. The iFrame gets created successfully, but when the function gets to the point where it needs to add the HTML in, it doesn't do it.
Here's my code:
function createiFrame(min, max, key) {
console.log("Max-Width", max);
//CREATING A CLASS FOR THE IFRAME
var iframeClass = key + "-" + max;
//var path = window.location.pathname;
//var page = path.split("/").pop();
//ADDING AN IFRAME INTO THE DOCUMENT AND ADDING ON THE CLASS
$(document).ready(function() {
$('<iframe>', {
width: max
}).addClass(iframeClass).prependTo('body');
});
var requiredHTML = document.getElementById('container').innerHTML;
console.log("RequiredHTML", requiredHTML);
//ADDING THE COLLECTED HTML INTO THE IFRAME -- THIS IS WHERE
//IT STOPS WORKING
$('.' + iframeClass).ready(function() {
console.log("iFrame ready");
$('.' + iframeClass).contents().find('body').html("<p>Testing it out</p>");
});
var $head = document.getElementsByTagName('head')[0].innerHTML;
$(document).ready(function() {
$('.' + iframeClass).contents().find('head').append($head);
});
}
EDIT
I've realised this problem is occurring because when I try to run that code, the DOM isn't ready yet.
So new question;
How do I get the DOM ready?
check out this post:
addEventListener to iFrame
you need to use
$('#myIFrame').load(function(){
....
$('.' + iframeClass).ready(function() {
...
}
Doesn't work because an iframe being ready doesn't trigger the Event 'DOMContentLoaded' which .ready() is waiting for. You'll need to refer directly to a document, which will then trigger the DOMContentLoaded event. This should work based on that.
$(iframeClass document).ready(function() {
...
}
I will stress though that this isn't something that I've tried before, but it seems like it's just a matter of which document you're referring to.
I am polishing a jQuery script to make a nice page-changing effect in my project. Here is my script...
$(document).ready(function() {
// Start-Up Page Load (Cover, ToC, etc.)
$('#content').load('pages/page1.htm');
// Navigating Pages
$(document).on('click', 'a', function(e) {
if ($(this).attr('target') == '_blank') {
return true;
}
else {
e.preventDefault();
}
var ahref = $(this).attr('href');
var $c = $('#content');
var $cc = $('#content_container');
$c.fadeTo('fast', 0.0, function() {
$cc.animate({height: 'hide'}, 500);
$c.load(ahref + '#content/', function(){
$cc.animate({height: 'show'}, 500, function(){
var x = $($c.children()[1]).children();
var waittime = 0;
for (item in x) {
item.delay(waittime).fadeTo('slow', 1.0);
waittime += 500;
}
// Failsafe that loads the entire page contents, in case the element-by-element animation does not work.
// $c.fadeTo('slow', 1.0);
});
});
});
return false;
});
});
What I need to do is go through each and every specific tag in the remote content. I do this first by setting the var waittime to a number to help adjust the delay times because the elements tended to load at the same time in earlier tries, then (supposed to) loop through each HTML element and fade each one in, one at a time.
I have used the .each() and .children() methods and got bad results on page load. Through trial and error, I found that this line:
$($c.children()[1]).children()
Fetches each top-level HTML tag as an object. But when I tried running through that code using either item or $(item), the console reads an error.
I don't think it gets text-nested elements like <span>. But if it did, doing so will interrupt the flow of the animation, so I don't know how jQuery can nitpick that out (though I noticed it doesn't, because it's reading the top-level). In other words, it should do the whole <p>, <span> inside it included, at the same time.
Taking the above into account, I noticed it did everything at the first level. It should be able to do whole elements nested inside whole elements individually, such as the <dt> and <dd> inside a <dl>.
How do these HTML objects being fetched work? For some reason the console is telling me things like .fadeTo() is not a function.
I used setTimeout with each to animate elements in sequence. my code is like below:
var panels = $(".panel");
panels.each(function(i) {
var $panel = $(this);
setTimeout(function () {
$panel.css("display", "block")
.addClass('fadeInDown animated')
.one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function () {
$panel.removeClass('fadeInDown animated');
});
}, i * 200);
});
you can use CSS3 animations and add classes to elements.
Have the following code:
$("#blogs").mouseover(
function () {
$(this).addClass("hover");
$("#home").removeClass("hover");
$("#homepages").removeClass("hover");
$("#apps").removeClass("hover");
$("#facebook").removeClass("hover");
$("#kontakt").removeClass("hover");
$("#content").hide().load("blogs.html", function(){
$("#content").show("slide");
});
});
Works all fine, but now I would like the load() / show() function only be called if #content does not already contain blogs.html.
In other words: I would like to check if blogs.html is already loaded and if yes, simply do nothing and only if not there yet I would load and show it.
Have tried some things with hasClass() and some if-formulas but struggle to get this check.
Tried stuff like this:
$("#content section").hasClass("check_blog").hide().load("blogs.html", function(){
$("#content").show("slide");
Basically I just need to know how I can check if blogs.html is already the contents of #content.
Thanks a lot for any help. Regards, Andi
Add an ID to some element in blogs.html, say blogsloaded, then you can check for it with:
if (!$("#blogsloaded").length)
$("#content").hide().load("blogs.html" ...
Another method would be to store in a variable if you already loaded it:
if (!this.blogsloaded)
{
this.blogsloaded=true;
$("#content").hide().load("blogs.html" ...
}
I would split up your mouseover events into two namespaced events. One which will only run once.
// This event will only run once
$("#blogs").on("mouseover.runonce", function () {
$("#content").load("blogs.html");
});
// because this event will unbind the previous one
$("#blogs").on("mouseover.alwaysrun", function () {
$(this).off("mouseover.runonce");
$(this).addClass("hover");
$("#home").removeClass("hover");
$("#homepages").removeClass("hover");
$("#apps").removeClass("hover");
$("#facebook").removeClass("hover");
$("#kontakt").removeClass("hover");
$("#content").hide();
});
Update a data attribute on #content that contains the url or id of the currently loaded content. Also, you should handle the case where the user hovers over a different section before the previous is done loading.
var request; // use this same var for all, don't re-declare it
$("#blogs").mouseover(function () {
// exit event if the blog is the current content in #content
if ( $("#content").data("current") == "blog") return;
$("#content").data("current","blog");
$(this).addClass("hover");
$("#home").removeClass("hover");
$("#homepages").removeClass("hover");
$("#apps").removeClass("hover");
$("#facebook").removeClass("hover");
$("#kontakt").removeClass("hover");
// if a previous request is still pending, abort it
if ($.isFunction(request.abort) && request.state() == "pending") request.abort();
// request content
request = $.get("blogs.html");
$("#content").hide();
// when content is done loading, update #content element
request.done(function(result){
$("#content").html(result);
});
});
I strongly suggest against using hover for loading content with ajax.
Also, in it's current form, this code is not very re-usable, you'll have to have one for each link. I suggest instead using classes and having only one event binding handling all of the links.
You can do it like this using .has() to detect descendants of content
$("#blogs").mouseover(
function () {
$(this).addClass("hover");
$("#home,#homepages,#apps,#facebook,#kontakt").removeClass("hover");
var $c = $("#content");
if($c.has('.check_blog')){ // if content contains an element with that class
$("#content").hide().load("blogs.html", function(){
$("#content").show("slide");
}
});
});
You could do something like this:
$("#blogs").mouseover(
function () {
$(this).addClass("hover");
$("#home").removeClass("hover");
$("#homepages").removeClass("hover");
$("#apps").removeClass("hover");
$("#facebook").removeClass("hover");
$("#kontakt").removeClass("hover");
if($('#content').html() == '') {
$("#content").hide().load("blogs.html", function(){
$("#content").show("slide");
});
}
});
Part of my HTML page is the following div:
<div id="images_wrapper">
<img ... />
<img ... />
<img ... />
...
</div>
Initially, this div is hidden, and I show it only when all images are loaded:
$(window).load(show_images_wrapper);
However, if I'm not mistaken, show_images_wrapper will be called only when all the page is loaded. I would like show_images_wrapper to be called as soon as all images inside images_wrapper has been loaded, and don't wait until all the page is loaded.
I tried:
$("#images_wrapper").load(show_images_wrapper);
but it didn't work.
How should I do this?
Set up a counter to the quantity of the images using the length[docs] property, that is decremented as the images load.
var imgs = $("#images_wrapper > img").not(function() { return this.complete; });
var count = imgs.length;
if (count) {
imgs.load(function() {
count--;
if (!count) {
$("#images_wrapper").show();
alert('all done');
}
});
} else {
$("#images_wrapper").show();
}
The the not()[docs] method is removing from the matched set the images where their .complete property is true. This means the image has already downloaded, and was perhaps cached by bhe browser.
Of course the load()[docs] method fires as each image finishes loading.
Example: http://jsfiddle.net/uhmAR/1/
EDIT: Changed it so that the container will show if all the images happened to be cached.
EDIT:
Another variation on the above is to bind the .load() to all the images, and use the filter()[docs] method to get the ones that are .complete, and just manually invoke the .load() on them.
This removes the need for the if/else statement.
var imgs = $("#images_wrapper > img")
var count = imgs.length;
imgs.load(function() {
count--;
if (!count) {
$("#images_wrapper").show();
alert('all done');
}
}).filter(function() { return this.complete; }).load();
Example: http://jsfiddle.net/uhmAR/3/
I wrote a jQuery plugin that can do this.
$('#images_wrapper').waitForImages(function() {
// Done.
});
Alternatively,
var images = $('#images_wrapper img'),
imagesLength = images.length;
images.load(function() {
if ( ! --imagesLength) {
// Done.
}
});