4

How to prevent jQuery $('body').load('something.php'); from changing any DOM till all the content from something.php (including images,js) is fully loaded

-Lets say some actual content is:

Hello world

And something.php content is:

image that loads for 10 seconds
20 js plugins

After firing .load() function nothing should happen, till images an js files are fully loaded, and THEN instantly change the content.

some preloader may appear, but its not subject of question.

[edit]----------------------------------------------------------------------

My solution for that was css code (css is loaded always before dom is build) that has cross-browser opacity 0.

.transparent{
    -moz-opacity: 0.00;
    opacity: 0.00;
    -ms-filter:"progid:DXImageTransform.Microsoft.Alpha"(Opacity=0);
    filter: progid:DXImageTransform.Microsoft.Alpha(opacity=0);
    filter:alpha(opacity=0);
}

And it prevent from bad flickr of content usually but not always. Now its possible to detect somehow that content is loaded by jQuery ajax and apply some show timeout and so on. But in fact question is still open.

To make now a little more simple example for that question:

How to begin changing DOM after $('body').load('something.php') with 3000ms delay after clicking the link that fire .load('something.php') function? (Browser should start downloading instantly, but DOM changing has to be initiated later)

user1785870
  • 477
  • 1
  • 6
  • 16
  • Is it a requirement that the content be loaded, or can there just be the appearance of loading. Meaning is it ok to hide the content until it is finished loading even though it exists in the document already? – Quentin Engles Dec 04 '12 at 13:12

4 Answers4

3

Use .get instead and assign the contents in the success callback:

$.get('something.php', function(result) {
    $('body').html(result);
});
Shadow The GPT Wizard
  • 66,030
  • 26
  • 140
  • 208
1

There are some implementation details you may have to solve yourself, but here's a rough solution:

  1. Don't use .load() directly. It can't be changed to wait for all images to load.

  2. Use $.get() to fetch the HTML into a variable, let's call it frag.

  3. Use $(frag).find('img').each(fn) to find all images and dump each this.src inside a preloader.

    var images = [], 
    $frag = $(frag),
    loaded = 0;
    
    function imageLoaded()
    {
        ++loaded;
        // reference images array here to keep it alive
        if (images.ready && loaded >= images.length) {
            // add $frag to the DOM
            $frag.appendTo('#container');
        }
    }
    
    $frag.find('img').each(function() {
        var i = new Image();
        i.onload = i.onerror = imageLoaded;
        i.src = this.src;
    
        images[images.length] = i;
    });
    
    // signal that images contains all image objects that we wish to monitor
    images.ready = true;
    

    Demo

  4. Once all images are loaded, append the earlier frag to the DOM using $frag.appendTo('#container').

Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
  • Very nice, but the `src` is changed near instantly. Will the image still load? – Asad Saeeduddin Dec 04 '12 at 11:08
  • Evidently not, the request gets interrupted. – Asad Saeeduddin Dec 04 '12 at 11:16
  • @Asad Perhaps the `images` array gets GC'd because there's no reference to keep it there ... – Ja͢ck Dec 04 '12 at 11:27
  • Figured it out, have a look at this demo if you're still interested: http://jsfiddle.net/B8B6u/1/ – Asad Saeeduddin Dec 04 '12 at 11:58
  • @Asad I still don't see a problem with [my code](http://jsfiddle.net/B8B6u/3/) though. – Ja͢ck Dec 04 '12 at 12:12
  • Yeah I just tested it, it works: http://jsfiddle.net/B8B6u/4/ I must have been making a mistake with the old one. – Asad Saeeduddin Dec 04 '12 at 12:17
  • But accidentally, what about other files, like js and many else. DOM should be changed when 'virtual' loading bar of it disappear. It should be universal. – user1785870 Dec 04 '12 at 19:07
  • @user1785870. You can do this with JavaScript as well, just use getScript() and strip out the script tags before pushing it into the DOM. CSS has to be done differently, each file needs to be loaded separately and evaluated. A server side component loader might benefit you. – Ja͢ck Dec 04 '12 at 22:38
0

Have you tried this?

$(function(){$('body').load('something.php')});

Edit: I just realized you are actually wanting to wait for the stuff to load before it get's placed in the body.

Here are three links to similar questions.

Preloading images with jQuery

Is it possible to preload page contents with ajax/jquery technique?

Preloading images using PHP and jQuery - Comma seperated array?

You can probably adapt those to scripts too.

This might work too.

  $.ajax({
    'url': 'content.php',
    'dataType': 'text',
    'success': function(data){
        var docfrag = document.createDocumentFragment();
        var tmp = document.createElement('div'), child;
        //get str from data here like: str data.str
        tmp.innerHTML = str;
        while(child = tmp.firstChild){
            docfrag.appendChild(child);
        }
        $('body').append(docfrag);
    }
  });

It's a longer way of doing what Shadow Wizard suggests, but it will probably work. Hm. Never mind. Jack's answer looks the best. I'll wait a while and if no one likes my answer I'll delete it.

Edit: It looks like appending to documentfragments can do http requests. Any script using createDocumentFrament may benefit from preloading.

In this question they want no http requests even though that's what createDocumentFragment is doing: Using documentFragment to parse HTML without sending HTTP requests.

I can't be sure if this is true for all browsers or just when the console.log is run, but it could be a good option for preloading if this behavior is universal.

Community
  • 1
  • 1
Quentin Engles
  • 2,744
  • 1
  • 20
  • 33
0

Here is a quick proof of concept that loads relevant images before inserting an HTML fragment into the DOM: http://jsfiddle.net/B8B6u/5/

You can preload the images using the onload handler to trigger iterations:

var images = $(frag).find('img'),
loader = $('<img/>');

function iterate(i, callback) {
    if (i > 0) {
        i--;
        loader.unbind("load");
        loader.load(function() {
            iterate(i, callback);
        });
        loader.attr('src', images[i].src);
    }else{
        callback();
    }
}

iterate(images.length,function(){
    $('#container').html(frag);
});

This should work, since each image is loaded after the previous one has finished loading.

Asad Saeeduddin
  • 46,193
  • 6
  • 90
  • 139
  • I've used your fiddle to host my own code which works equally well as can be seen [here](http://jsfiddle.net/B8B6u/3/). – Ja͢ck Dec 04 '12 at 12:18
  • what about js and many many other files that can be loaded to a page. But if we cant make it universal, how to check all the js files without adding callback to each of included to page js file. – user1785870 Dec 04 '12 at 19:10
  • @user1785870 Do you want to run all JS files before the DOM is modified? – Asad Saeeduddin Dec 04 '12 at 19:12
  • when you have div#myDiv and code $('#myDiv').hide(); and when you load page you wont see myDiv at all. When you change DOM before you LOAD js you will some flickr of it. – user1785870 Dec 05 '12 at 10:48