History API and browser historical stack management

Source: Internet
Author: User

Read Catalogue

    • History API Review
    • History API and business practices
    • Review

Mobile development has special needs in some scenarios, such as a spa architecture that is often used in some projects to improve user experience and speed response. Traditional single-page applications are based on URL hash values for routing, there is no compatibility problem, but there are drawbacks-for ie6-7 that do not support the Onhashchange property needs to set the timer to constantly check the hash value changes, performance is not very friendly.

Now, in mobile development, the HTML5 specification provides us with a history interface that allows you to manipulate historical records freely. This article does not introduce history interface in detail, but explores how the history interface affects the browser's historical stack, and uses this law to apply it to the actual business, proposing two kinds of historical record saving strategy, making the routing logic clearer and making the spa easier.

History API Review

The HTML5 history API consists of 2 methods: History.pushstate () and History.replacestate (), and one event: Window.onpopstate.

Pushstate

History.pushstate (stateobject, title, URL), including three parameters.

The first parameter stores the state object corresponding to the URL, which can be obtained in the Onpopstate event or in the History object.

The second argument is the title, which is not currently implemented by the browser.

The third parameter is the set URL. Generally set to a relative path, if set to an absolute path, you need to guarantee the same origin.

The Pushstate function pushes a record of a URL to a set value to the browser's history stack and changes the current pointer of the history stack to the top of the stack.

Here I use the history stack and the current pointer to illustrate the browser's management strategy for history. The document does not use such words, the author in order to more image of the interface to the browser history of the impact of the use of such a description, if there is a bad point please timely pointed out (but the current model based on the logic of the implementation of the paradox does not appear).

Replacestate

The interface is the same as the Pushstate parameter and has the same meaning. The only difference is that replacestate is the URL that replaces the current history of the browser history stack with the set. It is important to note that Replacestate does not alter the current pointer of the browser history stack.

Onpopstate

The event is a property of window. This event triggers the forward, backward, and execute History.forward, History.back, and History.go in the calling browser, because these operations have a common denominator that modifies the current pointer of the history stack. Without changing the document, the Onpopstate event is triggered once the current pointer changes.

History API and business practices

The most common single-page scenario: List page, item detail page, and other Link Entry page, comment page, and other product Details page. The above mentioned has involved 4 separate business logic pages (recommended product reusable Product Details page logic), namely: list, details, picture details and comments. Combine these 4 pages into one page, which is the simplest spa. For the user's good experience, must design reasonable interactive logic, the most intuitive is the browser (or mobile app, the public number) of the back and forward must conform to the business logic characteristics. Therefore, this involves the use of the history API and the browser's historical records management.

For the specific logic. On the list page, click on one of the items, here is Item 1, go to the details page. The details page includes a carousel map of the product, a picture details entry for the item, a comment entry, and other recommended merchandise entries. Proceed as follows: Go to the Picture Details page, go back to the Details page and enter the comment page; back to Product 1 details page and then from the recommended product entry into the Product 9 details page, also in the Product 9 details page into the Picture Details page and review page, and then back to the Product 9 details page , enter the product 34 details page by the recommended product entry, and perform similar operations. Finally, the product 34 image details page or review page can be successfully back to the original Product List page.

The bold "back" above means using the browser back button, or using the phone's own return, or using the Back button provided on the page.

Such a small demand, but it is not so easy to really let go. Only according to the History API 2 functions and an event to blindly try to achieve, this is elephant, robustness is not high. Not knowing the browser's history management policy and not understanding the current page history count, this scenario is a bit of a hassle to implement. So before you write your business code, you need to understand how the pushstate and replacestate of history affect the historical stack in particular.

Explore the relationship between the browser history policy and the historical API

Because the browser does not provide a specific interface for each page's history, all tests are black-box. But in the mobile side, mostly is the WebKit kernel, its WebCore implementation is also similar, so the conclusion of this section can be used on the mobile side completely.

Although the current page's history stack cannot be accessed, the browser provides the History.length property, which indicates the number of current history stacks. This value will help us to better analyze the impact of the history API on the historical stack.


As the test instance. Where the white arrow means clicking on the link and performing the Pushstate action (that is, action 1), the black Arrow executes the browser back, the red dot is the current pointer in the history stack, and each item is the history stack, and the number of history is the count of its children.

Initially on the first Search list page, the number of history stacks increases after performing action 1, and the current pointer moves up one bit to 26788.html;
Similarly, in the execution of 3 operations 1, the history stack increments 3, the current pointer is still at the top of the stack, that is, 78099.html;
After that, the browser back, the number of history stack is unchanged, the current pointer moves down one bit to 8819.html;
In this place the operation 1, the top element of the stack changes, the current pointer moves to the top of the stack, the number of historical stacks unchanged;
Proceed to Operation 1, the top element of the stack changes, the pointer moves to the top of the stack, the number of historical stacks plus one;
Perform browser back, stack top element is unchanged, pointer moves down one to 8128.html, the number of historical stacks is unchanged;
Perform browser back, stack top element is unchanged, pointer moves down one to 8819.html, the number of historical stacks is unchanged;
Perform browser back, stack top element is unchanged, pointer moves down one to 8128.html, the number of historical stacks is unchanged;
Perform browser back, stack top element is unchanged, pointer moves down one to 26788.html, the number of historical stacks is unchanged;
Performing action 1, the top element of the stack becomes 9721.html, the pointer moves up to the top of the stack, and the number of history stacks becomes 3;
Performing action 1, the top element of the stack becomes 8387.html, the pointer moves up to the top of the stack, and the number of history stacks becomes 4;
Perform browser back, stack top element is unchanged, pointer moves down one to 9721.html, the number of historical stacks is unchanged;
Perform browser back, stack top element is unchanged, pointer moves down one to 26788.html, the number of historical stacks is unchanged;
Perform browser back, stack top element is unchanged, pointer moves down one to search.html, the number of historical stacks is unchanged;
Performing action 1, the top element of the stack becomes xxx.html, the pointer moves up to the top of the stack, and the number of history stacks becomes 2;
...

This concludes the experiment. Although this is the only test case listed here, I actually did more and more complex tests, and the platform involved both the PC and the mobile browser, and the native WebView, the same results. This series of tests illustrates a number of problems, and one of the concluding sentences is:

The browser maintains a history stack for each page. Execute the Pushstate function to press the set URL to the top of the stack, while modifying the current pointer;
When the back operation is performed, the history stack size does not change (history.length unchanged), only the position of the current pointer is moved;
If the current pointer is in the middle of the history stack (not the top of the stack), executing pushstate will change the size of the history stack.
Summing up the law of pushstate, it can be found that the current pointer executes pushstate at the top of the history stack, which increases the size of the history stack, and if the current pointer is not at the top of the stack, the item is added in the position. Performing a back operation does not modify the history stack size, so you can move freely through the back and forward in the current size of the history stack.

Master This rule, know how to maintain the history, know in what state need pushstate. Back to the original demand, the product manager stipulated that the review page of the item 34, press the Back button to reach the original list page, but he did not specify how to retreat. There will be 2 implementations in this way:

    • Every time you go back, you return to the last place you visited. For example, on the review page of item 34, you will be back to the details page of item 34, and then back to the details page of item 9 until you return to the list page.
    • Maintain a total of three levels of history, the first layer (bottom) is the list page, the second layer is the Detail page, the third layer (the top of the stack) is a comment page or Picture detail page. With this implementation, the comment page of item 34 is first backed up to the details page of item 34 and the second back to the list page.

For the first, the implementation is the simplest, because this is entirely the default control of the history stack by the browser, and we just need to call pushstate at the right time to insert the URL into the stack, and then listen for the corresponding time in the Onpopstate handler function:

Window.addeventlistener (' Popstate ',function (E) {Console.log (' Popstate ')Back (forward) to the Product Details page, load data asynchronously and renderif (E.state && e.state.indexof ('/shop/sku/')!==-1) {Ajaxdetail (e.state,true); }ElseBack (forward) to comment page, loading data rendering asynchronouslyif (E.state && e.state.indexof ( '/shop/comment/commentlist.html ')!== -1) {ajaxComment ( E.state,true); }else //back (forward) to Picture details page, asynchronously loading data rendering  if (e.state && e.state.indexof ( '/shop/item/pictext/')!== -1) {ajaxpic (E.state,true);} else //back (Forward) to list page, hide floating layer if ( E.state && e.state.indexof ( '/search/')!== -1) {//hide the floating layer of the spa $ ( '. Spa-container '). CSS ( ZIndex ',  '-1 '); } }); 

For the second implementation, it is the focus of this article. After all, the historical stacks maintained by the browser by default do not match in some business scenarios, so developers need to maintain a history stack themselves. In this implementation, because of a total of 4 page displays, we have set a 3-layer history stack, which is well understood.

To build such a history stack, you need to add two additional history to the main page (that is, the list page). This is because the URL of the current page is added to the history stack when the list page is opened by default.

function push(state){ history.pushState(state, null, location.pathname + location.search); } // ‘abc‘用于标示初始列表页 history.replaceState(‘abc‘,null,location.pathname + location.search) // 压入两条历史记录 push(); push();

This way, 3 history is created when the list page is opened, and the URLs for the 3 history are the URLs of the list pages, which have no effect on subsequent operations.

Opening the details page in the list page requires additional processing. As we design the history stack, the second layer should be the detail page, and at this point the current pointer of the history stack is pointing to the top of the stack after initialization, so you need to move the current pointer down one bit. We need history.back to do it here.

$('. Item-list '). On (' Click ',' A ', handler);Loading detail data asynchronouslyvar handler =functionE,isscrollxclick) {var a =This Ajaxdetail ($ (a). attr (' href '), isscrollxclick);Returnfalse;};var Isscrollxclick;/** * @params: URL request path Isscrollxclick: Click on the recommended item * */var ajaxdetail =functionUrl,isscrollxclick) {$.ajax ({URL:'/api ' + URL, success:functionData) {...if (!isscrollxclick) {Console.log (' I am back! ')The back or forward in the code does not immediately start the Popstate event, taking the V8 engine as an example, after performing the backThe event is triggered after about 18us, and if the URL is immediately modified via replacestate, it will cause a failure, and the modifiedThe URL of the top of the history stack stack.Here the Replacestate compatible History.back () is executed asynchronously; }//Asynchronous Trigger SetTimeout (function ( Span class= "Hljs-params") {history.replacestate (URL, null, URL);}) //for the product of the recommendation bar, loop-bound event, where the event proxy is optimized for $ ( ' #J_PDSlider '). On (  ' click ',  ' a ',  function (e) {Isscrollxclick = 1; Handler.call (this,e,isscrollxclick); return false;}); }, Error: function (xhr, type) { Alert ( ' Ajax error! ')}) }; 

Implemented here, through the Isscrollxclick variable to determine whether the click is the recommended product, if not you need to perform a back operation, move the pointer down. At this point the pointer refers to the second layer, but the URL of the browser and the second-level history is still the URL of the initialization setting, so it needs to be modified, where the current URL is modified asynchronously.

The asynchronous execution of Replacestate is determined by the WebKit triggering popstate event. Executing history.back or History.forward in code does not immediately return, nor does it immediately trigger the Popstate event. Since the WebKit source is not read, it is impossible to speculate on what extra action is required after back or forward, and there is a 10us level interval between them, so the URL must be changed asynchronously using the settimeout implementation.

In the specific development process, this problem puzzled the author for several days, and finally in a debugging process to find the browser URL changes, only to think of the event may be triggered by the time difference caused.

The logical handling of picture details and comments is similar to the above, needless to say.

The last fallback needs to go back to the list page, and in the initialization phase we set the state to "ABC" for the list page, specifically marking the route, so in popstate event processing, we can go back to the initial page based on that item:

 window.addeventlistener ( ' Popstate ', function (e) {if (e.state && e.state.indexof ( '/shop/sku/')!== -1) {ajaxdetail (E.state,true);} else if (e.state && e.state.indexof ( ' abc ')!== -1) {//hide the floating layer of the spa $ ( Spa-container '). CSS ( ' ZIndex ',  '-1 '); push (); Push (); } }); 

If you go back to the initial page, hide the floating layer while performing 2 push operations. According to the rule found in the previous section, 2 push operations are performed on the initial page, the 2 history is re-added at the current pointer position, the current pointer points to the top of the stack, the number of history stacks remains unchanged, and still 3. This completes a simple spa system that is customized to maintain the history stack by the developer.

Review

This article is written entirely by accident, because of the various needs of the actual project, we should not just focus on the use of the API level. In addition, in the development process encountered difficult to solve the problem, we need to put forward a variety of reasonable ideas and detailed experiments to prove that the corresponding conclusions need to be used to illustrate other scenarios, so as to ensure the reliability of the solution. There is no way to manually maintain the history stack on the Web or in the book, nor does it clearly indicate how the history API is affected by the browser's historical record, so this article is instructive for developers who are aiming to implement the SPA with the Legacy API.

History API and browser historical stack management

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.