Recently, I am learning backbone. When learning to understand backbone, you must first understand spa. When understanding spa, you must first understand how single-page applications can change the URL without refreshing the page.
Compared with jumps to different pages, AJAX can be said to have greatly improved the user's browsing experience. It is very pleasant not to see the white screen between page switching. However, many earlier AJAX applications did not support the browser's forward and backward movement, which caused users to immediately return to their original position no matter where they browse on the website, and users could not switch browsing history through the browser's forward and backward movement button.
For the first problem, it is quite easy to solve. Just use cookies or localStorage to record the application status. Read this status when refreshing the page, and then send the corresponding ajax request to change the page. But the second problem is very troublesome, let’s talk about the solution of modern browsers first.
HTML5 Solution
To understand how HTML5 can advance and backward, you must first understand history objects and location objects.
history object
History object properties
1.length: Returns the number of URLs in the browser history list. For each page that the user visits on the current tag, this number is added by 1. Due to privacy reasons, the specific URL content is not visible.
2.state: Objects related to the current URL can only be added or modified through pushState and replaceState. We can use it to store information related to urls.
History object method
1.history.back()
This method has no parameters. After triggering, it will return to the previous page browsed, which is equivalent to clicking the back button of the browser.
2.history.forward()
This method has no parameters. After triggering, it will return to the page you browse before you browse, which is equivalent to clicking the browser's forward button.
3.history.go(number)
This method accepts a shaping variable parameter, history.go(-1) is equivalent to backing a page, history.go(1) is equivalent to advance a page, history.go(0) will refresh the current page.
4.history.pushState(state, title, url)
The key to changing the URL and not refreshing the page is it. This method will change the location.href of the current page and modify the current history.state object. After execution, history.length will be increased by 1. This method accepts three parameters.
1.state: The object related to the current URL.
2.title: Page title, but all browsers ignore it. To change the title, you still need to use document.title.
3.url: A URL with the same domain as the current page, location.href will become this value.
5.history.replaceState(state, title, url)
This method is the same as above, but it will not change history.length, but will only modify history.state and location.href.
Note that the third parameter pushState and replaceState cannot cross-domain and will not trigger the browser's popstate event and onhashchange event (tested under chrome33).
location object
In addition to clicking the forward/back button and history event, you can also change the Url through the location method and modify the location properties:
Properties of location object (read and write):
1.host:Domain name + port number
2.hostname:Domain name
3.port:port number
4.protocol:Protocol
5.href: full path
6.origin: protocol + domain name + port
7.hash: URL (hash) starting with the pound sign (#)
8.pathname: Document path + Document name
9.search:(?) The following content
You can achieve the purpose of refresh-free by changing location.href or location.hash.
Methods of location object:
1.assign: Change the value of the url and add the current url to the history.length will increase by 1. location.assig('#' + x) will change the url but will not refresh the page.
2.reload: Refresh the page.
3.replace: Change the value of url, but history.length remains unchanged. The same method of use assign.
popstate event
When the url changes, for example, if the user clicks the forward/back button, history.go(n) (n does not equal 0), location.hash = x(x does not equal the current location.hash) will trigger this event. You can use it to monitor the url to implement various functions.
The code copy is as follows:
window.onpopstate = function(){
//do sth
}
onhashchange event
Changing the hash value will trigger the popstate event, and triggering the popstate event will not necessarily trigger the onhashchange event. After testing:
1. Hash changes but location.pathname remains unchanged will trigger onhashchange event, such as history.pushState(", ”, '#abc');
2. If hash and location.pathname change together, it will not trigger, such as history.pushState(", ”, 'a#abc');
How to write old browsers
Old browsers do not support pushState and replaceState, so the way to monitor URL changes through popstate (in fact, it does not support this method). Then you can only change the content behind url# to achieve no refresh, but they do not support onhashchange, so you are indifferent to changes in url (except that the page will scroll to the corresponding id position of the page). Then you can only use the ultimate move: poll, and setInterval to listen to the value of the url. Like this:
The code copy is as follows:
var prevHash = window.location.hash;
var callback = function(){...}
window.setInterval(function() {
if (window.location.hash != prevHash) {
prevHash = window.location.hash;
callback(prevHash);
}
}, 100);
Of course, this writing is very frustrating. If you do not consider clicking on the a tag with id on the page to change the hash, you can use the design pattern to elegantly implement the monitoring url. For example, the classic observer pattern uses a class to implement the function of changing hash, and then all classes (observers) that want to listen to the changes in the url to subscribe to this (observer) class.
The code copy is as follows:
//Change the url class
function UrlChanger() {
var _this = this;
this.observers = [];
//Add an observer
this.addObserver = function(obj) {...}
//Delete the observer
this.deleteObserver = function(obj) {...}
//Notify observers
this._notifyObservers = function() {
var length = _this.observers.length;
console.log(length)
for(var i = 0; i < length; i++) {
_this.observers[i].update();
}
}
//Change the url
this.changeUrl = function(hash) {
window.location.hash = hash;
_this._notifyObservers();
}
}
// Listening class
function oneOfObservers() {
var _this = this;
this.update = function() {...}
}
//accomplish
var o1 = new UrlChanger();
var o2 = new oneOfObservers();
o1.addObserver(o2);
o1.changeUrl('fun/arg1/arg2/');
//o2 has do sth...