pushState() and popState(): manipulating browsers' history
Asked Answered
H

2

6

I am working on a small project in which I want to create an Ajax-style website. Content is loaded with jQuery's load(). As some of you know, the down side of this is that the URL does not change accordingly to the content that is displayed. To make this work you can use pushState(). Because this is not cross-browser supported, I used the plug-in history.js.

This is working quite nicely, but the problem is that my back and forward buttons are not working as they should and I have no idea what I am doing wrong. The URL is changing correctly and the content is as well, but the title is not changing at all. With title I mean what is shown as title of the window (or tab) of the browser window. I.e. the <title> of the page that is loaded OR the title attribute of the link that refers to that page (they are the same). I think it has to do with the fact that I call only a section of the page whose title I need (i.e. by adding #content to the load()). I still want the title of that page though. Here is a test case of what I have up to now. (Not available any more since 28 Dec. 2013.) jQuery file:

$(document).ready(function () {
    var firstLink = $("ul > li:first-child > a");
    var menuLink = $("ul > li > a");
    var firstLoadedHtml = firstLink.attr("href") + " #content";

    $("#sectionContainer").hide().load(firstLoadedHtml).fadeIn("fast");
    firstLink.addClass("active");   

    menuLink.click(function(e) {
        history.pushState(null, null, this.href);

        e.preventDefault();
        e.stopImmediatePropagation();

        var CurLink = $(this);
        var newLoadedHtml = CurLink.attr("href") + " #content";
        var oldSection = $(".contentPage");

        $("#sectionContainer").hide().load(newLoadedHtml).fadeIn("fast");
        menuLink.removeClass("active");
        CurLink.addClass("active");
    });

    $(window).bind('popstate', function() {
            var returnLocation = history.location || document.location;

            $('#sectionContainer').load(returnLocation + "#content");
    });
});

I also noted that when going back to the basic page (i.e. /sectionLoaderTest/) it goes blank for a while and only after a while the content of the first page is loaded. Is there a way to optimize this?

Additionally, I am using jQuery to add an active-class to the link that has been clicked. How can I make sure that this changes accordingly to the history? So that the correct link is highlighted when a user navigates back?

So the three questions are:

  1. Get the title working when going forward and backward
  2. Optimize the load time of the mainpage
  3. Fix the active-class when going forward and backward

All help welcome!

NOTE 1: I would like to build on history.js and my own script and as such keep support for < HTML5 browsers. I would prefer this because 1. I know this has to work, there is just something that's going wrong. 2. It would save time if I could see one's solution and implement it in the script I already wrote rather than figuring out a whole new script.

NOTE 2: The bounty will only be given to he or she who can answer all of my questions (3)!

Harriet answered 17/5, 2012 at 8:51 Comment(5)
Do you still not right, see an example of such here #6622949Tying
Your comment isn't really helpful. I am not needing to implement history.js (yet), I'm just trying to understand what pushState is doing for now. I want to get this problem fixed.Harriet
My comment points out your mistake, how to siting methods of processing the event. The method of intercepting the event popstate, you have posted is not correct, remove it from the method works off when clicked.Tying
Added history.js, still not workingHarriet
With title I mean what is shown as title of the window (or tab) of the browser window. I.e. the <title> of the page that is loaded OR the title attribute of the link that refers tot that page (they are the same).Harriet
M
11

Answer 1 - Get the title working when going forward and backward

As far as I understood you want to change the document title html from the link is loaded in a div. When you're loading a file in a div via jQuery .load you're just inserting the complete response text after an ajax request in it. So with all the stuff which shouldn't be in a div like the title or meta tag. However the title of the current document (http://bramvanroy.be/sectionLoaderTest/index.html) is defined in the title which is inside the head tag and not in the title tag inside a div so that's why the document title doesn't change.

So how can I fix it?

Good question, because the title element inside a div element is invalid, most browsers will remove it so you can't just use this:

document.title = $("#sectionContainer title").text();

because the title element may not exist.

So what we need is the direct response from the jQuery .load function. I can get it by adding the callback argument to the function:

$("#sectionContainer")
    .hide()
    .load(newLoadedHtml, function(responseText) {
        document.title = $(responseText).filter("title").text();
    })
    .fadeIn("fast");

What you may not understand is why I use the .filter and not the .find function. This is because jQuery is kind of parsing html, head and body tags out of the html but not removing there child elements.

Answer 2 - Optimize the load time of the mainpage

A document is getting loaded from to top to the bottom.

So first of all the css, JavaScript etc. should be loaded and then the the main document. Because jQuery is always waiting for the document before it's executing it wouldn't be a very bad idea to put all your script elements right before the end of the body, so that your HTML can be loaded before.

BtW I just had this problem at the first time after that everything was cached and the document was loading very fast.

Answer 3 - Fix the active-class when going forward and backward

I would say loop trough the href attributes of the a elements and compare it with the data from History.getState(). Should be easy.

You did some mistakes in your code - Your fixed code:

You appended the hash #content to all URLs.. it doesn't make sense and it will be removed by jQuery again: https://github.com/jquery/jquery/blob/master/src/ajax.js#L617 (Comes from lines: 158, 190, 337, 617 - rhash is on line 6)

$(document).ready(function () {
    var firstLink = $("ul > li:first-child > a");
    var menuLink = $("ul > li > a");
    var firstLoadedHtml = firstLink.attr("href");

    $("#sectionContainer").hide().load(firstLoadedHtml).fadeIn("fast");
    firstLink.addClass("active");   

    menuLink.click(function(e) {

        e.preventDefault();
        e.stopImmediatePropagation();

        var newLoadedHtml = $(this).attr("href");

        History.pushState(null, newLoadedHtml, newLoadedHtml);

        $("#sectionContainer")
            .hide()
            .load(newLoadedHtml, function(responseText) {
                document.title = $(responseText).filter("title").text();
            })
            .fadeIn("fast");
    });

    History.Adapter.bind(window, "statechange", function() {
        menuLink.removeClass("active");
        $("a[href='" + History.getState().title + "']").addClass("active");
        $('#sectionContainer').load(document.location.href, function(responseText) {
            document.title = $(responseText).filter("title").text();
        }); 
    });
});
Mabelmabelle answered 19/5, 2012 at 16:37 Comment(7)
I might be missing out on something (quite busy with a lot of things at the same time) but it doesn't work. Title on changes on the first and last link to the one that's defined in the 'includes'. Also (and more importantly) the back buttons don't change the content anymore. I have a feeling it is a very basic thing I am looking over. Test case has been edited: bramvanroy.be/sectionLoaderTestHarriet
The first page (Home) has got a good title on the document all the other pages have got the title Untitled Document as title. Sorry you're right it doesn't change the content on history back/forward anymore. It's because document.location is an object and you need a string, before it was document.location + "#content" That returned a string. So you need document.location.href, look at my edit.Mabelmabelle
After all I've made a new edit which will work! TESTED Even the correct link will always have the class active!Mabelmabelle
It works indeed, very big thanks to you! One more question (I'll give you the +100 anyway - no worries): the menu and its toggling is controlled by cookies. For some reason, however, the 'cookie' object gets deleted. You can try this by doing the following: click in a random link, hide panel, go to previous page, try and open the panel again. Check console for details. Very strange! I didn't know what effect all of this fiddling had on the cookies of a website ... You think this can be fixed? Thanks for everything!Harriet
Ow, something's not right yet: when going back the title of the tab is the relative URL, not the title that it ought to be!Harriet
@BramVanroy ohh yes you're right man. I forgot to add the callback function to the second .load function.Mabelmabelle
let us continue this discussion in chatHarriet
D
0

You could also try available routing plugins like davis.js or jQuery Pusher https://github.com/salvan13/jquery-pusher

Doughy answered 10/7, 2013 at 7:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.