1

I'm re-writing a website for my father's driving school, changing the unnecessary iframes and images to html5 and css3 markup, and improving the functionality trough javascript and jquery.

Now, I have a full page for every menu-item (for the search crawlers), while the header and the menu mostly stay the same. That's why I want to use the history api and change the content and the document title and description when a link gets clicked on a device with javascript enabled, so there doesn't have to be a full page refresh.

What works: - getting the #content div of the target page and loading it into the current page - using the history api to show a new url and the popstate event to return to previous states - changing the content of the html head from the current document

What doesn't work: - getting the html head from the target document

There should be a simple function for this, right? Or a good way to do this?

4
  • 1
    Not really, tags such as <head>, <body>, and <html> are omitted from the content when you parse it as html with jQuery. I suggest targeting specific tags instead, or parsing it as a string first to get only the content between <head></head> Commented Mar 12, 2013 at 14:53
  • 2
    Code, code, wherefore art thou, code? Commented Mar 12, 2013 at 14:53
  • What content do you need from the <head/>? I'm assuming you're wanting to tap in so you can get the <title/> of each page - document.title Commented Mar 12, 2013 at 14:55
  • I know I can get the document.title, but I want to be able to change everything in the head (description, keywords, etc...). Commented Mar 18, 2013 at 16:38

2 Answers 2

1

Since it's on the same origin, you can do that. It's a bit awkward, though it's not a million miles off what jQuery does behind-the-scenes when you use its ajax load (I expect jQuery uses a document fragment rather than an iframe).

Basically, you can create an off-page iframe, load the content page into there, and then extract whatever you need from it, something like this (completely unoptimized):

// I'm assuming you have a variable containing the content URL; I'll use `page`
var page = "/ocazuc/2";

// Create the iframe, put it off-page, and put it in the DOM
var iframe = $('<iframe>');
iframe.css({
  position: "absolute",
  left: -10000
}).appendTo(document.body);

// Hook the load event on it
iframe.load(function() {
  // Get the document
  var $doc = $(iframe[0].contentDocument.documentElement);

  // Steal its content
  $doc.find("body").contents().appendTo(document.body);

  // And use its title or whatever else you want from the `head`
  document.title = $doc.find('title').text();

  // Done with it
  iframe.remove();
});

// Start loading it
iframe[0].src = page;

Live Example | Source

Sign up to request clarification or add additional context in comments.

1 Comment

This may not be much more performant than a full page refresh, since you're still doing lots of the things involved in a full page refresh, e.g. setting up a whole new window environment and JS scope, fetching and parsing JS, CSS, images, videos, other iframes, etc. Ideally, you'd want to pull in that content as inert, detached DOM elements.
0

See my comments under the accepted answer. Basically, there are some performance concerns using that approach, which made me look for a better approach. To that end, I adapted an approach found in the rails turbolinks project, which is MIT licensed and written coffeescript. Credit goes to them for working around some of the browser compatibility issues.

https://github.com/rails/turbolinks

/*
 * A function that takes a string of HTML and returns a document object.
 */
var parseDocument = (function() {
    function createDocumentUsingParser(html) {
        return (new DOMParser()).parseFromString(html, 'text/html');
    }
    function createDocumentUsingDOM(html) {
        var doc = document.implementation.createHTMLDocument('');
        doc.documentElement.innerHTML = html;
        return doc;
    }
    function createDocumentUsingWrite(html) {
        var doc = document.implementation.createHTMLDocument('');
        doc.open('replace');
        doc.write(html);
        doc.close();
        return doc;
    }
    /*
     * Use createDocumentUsingParser if DOMParser is defined and natively
     * supports 'text/html' parsing (Firefox 12+, IE 10)
     * 
     * Use createDocumentUsingDOM if createDocumentUsingParser throws an exception
     * due to unsupported type 'text/html' (Firefox < 12, Opera)
     * 
     * Use createDocumentUsingWrite if:
     *  - DOMParser isn't defined
     *  - createDocumentUsingParser returns null due to unsupported type 'text/html' (Chrome, Safari)
     *  - createDocumentUsingDOM doesn't create a valid HTML document (safeguarding against potential edge cases)
     */
    var parser;
    if (window.DOMParser) {
        try {
            var testDoc = createDocumentUsingParser('<html><body><p>test');
            if (testDoc && testDoc.body && testDoc.body.childNodes.length === 1) {
                parser = createDocumentUsingParser;
            }
        } catch(ex) {
            parser = createDocumentUsingDOM;
        }
    }
    if (!parser) {
        parser = createDocumentUsingWrite;
    }
    return parser;
})();

Then presumably for example you could do something like:

var doc = parseDocument(wadOfHtml);
var title = doc.title;
var $content = $(doc).find('#content');

Be warned, I haven't verified for myself whether this code works, I'm going off of the reputability of the rails project. I also haven't verified whether my adaptation from coffeescript didn't introduce syntax or other errors. I literally haven't even run it yet; I'm merely optimistic.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.