Lifehacker implemention of url change with Ajax

2019-03-30 18:34发布

问题:

I see that Lifehacker is able to change the url while using AJAX to update part of the page. I guess that can be implemented using HTML5 or history.js plugin, but I guess lifehacker is using neither.

Does any one has a clue on how they do it? I am new to AJAX and just managed to update part of the page using Ajax.


Thank you @Robin Anderson for a detailed step by step algo. I tried it and it is working fine. However, before I can test it on production, I would like to run by you the code that I have. Did I do everything right?

<script type="text/javascript">  
var httpRequest;  
var globalurl;
    function makeRequest(url) {  
    globalurl = url;
    /* my custom script that retrieves original page without formatting (just data, no templates) */
    finalurl = '/content.php?fname=' + url ;

    if(window.XMLHttpRequest){httpRequest=new XMLHttpRequest}else if(window.ActiveXObject){try{httpRequest=new ActiveXObject("Msxml2.XMLHTTP")}catch(e){try{httpRequest=new ActiveXObject("Microsoft.XMLHTTP")}catch(e){}}}  

    /* if no html5 support, just load the page without ajax*/
    if (!(httpRequest && window.history && window.history.pushState)) {     
            document.href = url;
            return false;  
    } 

    httpRequest.onreadystatechange = alertContents;  
    alert(finalurl);    /* to make sure, content is being retrieved from ajax */
    httpRequest.open('GET', finalurl);  
    httpRequest.send();  
    } 

    /* for support to back button and forward button in browser */
    window.onpopstate = function(event) {
            if (event.state !== null) {
                    document.getElementById("ajright").innerHTML = event.state.data;
            } else {
                    document.location.href = globalurl;
                    return false;
            };
    };    

    /* display content in div */
    function alertContents() {  
            if (httpRequest.readyState === 4) {  
            if (httpRequest.status === 200) {  
                    var stateObj = { data: httpRequest.responseText};
                    history.pushState(stateObj, "", globalurl);     
                    document.getElementById("ajright").innerHTML = httpRequest.responseText;
            } else {  
                    alert('There was a problem with the request.');  
            }  
            }  
    }  
    </script> 

PS: I do not know how to paste code in comment, so I added it here.

回答1:

It is not an requirement to have the markup as HTML5 in order to use the history API in the browser even if it is an HTML5 feature.

One really quick and simple implementation of making all page transistions load with AJAX is:

  1. Hook up all links except where rel="external" exist to the function "ChangePage"
  2. When ChangePage is triggered, check if history API is supported in the browser.
  3. If history API isn't supported, do either push a hashtag or make a normal full page load as fallback.
  4. If history API is supported:
    1. Prevent the normal link behaviour.
    2. Push the new URL to the browser history.
    3. Make a AJAX request to the new URL and fetch its content.
    4. Look for your content div (or similar element) in the response, take the HTML from that and replace the HTML of the corresponding element on the current page with the new one.

This will be easy to implement, easy to manage caches and work well with Google's robots, the downside is that is isn't that "optimized" and it will be some overhead on the responses (compared to a more complex solution) when you change pages.

Will also have backward compatibility, so old browsers or "non javascript visitors" will just get normal page loads.

Interesting links on the subject

  • History API Compatibility in different browsers
  • Mozillas documentation of the History API

Edit:

Another thing worth mentioning is that you shouldn't use this together with ASP .Net Web Forms applications, will probably screw up the postback handling.

Code addition:

I have put together a small demo of this functionality which you can find here.

It simply uses HTML, Javascript (jQuery) and a tiny bit of CSS, I would probably recommend you to test it before using it. But I have checked it some in Chrome and it seems to work decent.

Some testing I would recommend is:

  • Test in the good browsers, Chrome and Firefox.
  • Test it in a legacy browser such as IE7
  • Test it without Javascript enabled (just install Noscript or similar to Chrome/Firefox)

Here is the javascript I used to achieve this, you can find the full source in the demo above.

/*
    The arguments are:          
        url: The url to pull new content from
        doPushState: If a new state should be pushed to the browser, true on links and false on normal state changes such as forward and back.
*/
function changePage(url, doPushState, defaultEvent)
{
    if (!history.pushState) { //Compatability check
        return true; //pushState isn't supported, fallback to normal page load
    }

    if (defaultEvent != null) { 
        defaultEvent.preventDefault(); //Someone passed in a default event, stop it from executing
    }

    if (doPushState) {  //If we are supposed to push the state or not
        var stateObj = { type: "custom" }; 
        history.pushState(stateObj, "Title", url); //Push the new state to the browser
    }               

    //Make a GET request to the url which was passed in
    $.get(url, function(response) {             
        var newContent = $(response).find(".content");      //Find the content section of the response
        var contentWrapper = $("#content-wrapper");         //Find the content-wrapper where we are supposed to change the content.
        var oldContent = contentWrapper.find(".content");   //Find the old content which we should replace.

        oldContent.fadeOut(300, function() { //Make a pretty fade out of the old content
            oldContent.remove(); //Remove it once it is done
            contentWrapper.append(newContent.hide()); //Add our new content, hidden
            newContent.fadeIn(300); //Fade it in!
        });

    });
}


//We hook up our events in here
$(function() {
    $(".generated").html(new Date().getTime()); //This is just to present that it's actually working.

    //Bind all links to use our changePage function except rel="external"
    $("a[rel!='external']").live("click", function (e) { 
        changePage($(this).attr("href"), true, e);
    });

    //Bind "popstate", it is the browsers back and forward
    window.onpopstate = function (e) { 
        if (e.state != null) {
            changePage(document.location, false, null); 
        }
    }
}); 


回答2:

The DOCTYPE has no effect on which features the page can use.

They probably use the HTML5 History API directly.