window.location.hash refresh in Chrome? - javascript

I was doing some snooping on the web and found window.location.hash = "etc" to be a widely adopted method to update the browser's location without reloading / refreshing the page. I've applied that to this example I've cooked up: http://dl.dropbox.com/u/1595444/locationExample/index.html
Works well in Safari, but...
What I've noticed is that in Chrome 10+ upon changing hash:
There is something similar to a reload.
The resulting symptom is a hiccup as the user scrolls down or up.
My console output is preserved (if you check your console the project string's are outputted).
The favicon seems to be reloading.
Has anyone run into this problem before? Know a fix?

There are most likely two things going on here:
The favicon and stop/refresh buttons flicker because of a Chrome bug (that mentions pushState, but hash changes are
on the same code path).
The slight hiccup when scrolling is because Chrome does a full page repaint and high-quality scale to update the page thumbnail, since it's considering hash changes as generating a new URL. That's also a bug. You can see this in the inspector timeline view, most scroll events result in a repaint of window width x some small height, but occasionally there will be a full-window repaint. This blog post has a few more details.
A workaround for both would be to defer the updating of the hash until the user is done scrolling (you can still update the white bar that appears under the current item immediately). You can do this by having something like:
var scrollTimeout;
window.onscroll = function() {
// update current item display here
if (scrollTimeout)
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(function() {
scrolTimeout = undefined;
// update hash here
}, 100);
};
Since it looks like you're using jQuery, there are debouncing plugins that may be helpful.

I don't have a definitive answer, but first I would try:
Prepending the hash mark (#) on to the value (i.e. use window.location.hash = "#etc").
Register a handler for the window.onhashchange handler.
Alternatively, you might consider using history.pushState if what you are trying to accomplish is make the back button return to the previous logical location (it's not clear to me what you are trying accomplish, whether you just want to jump to a section on the page, or something more complex).

var r='#hello';
if(navigator.userAgent.indexOf('Chrome/')!=-1){
top.history.pushState("", "", r);
return;
};
if(r.charAt(0)=='/'){
top.location.replace(r);
}else{
top.location.hash=r;
};
Worked for me. And it actually took me a long time to figure this out. Firefox also supports the history object now, so we may be able to get rid of the whole "hash" thing in a few years.
EDIT: Yes, the reloading thing is a Chrome bug.

Related

How can I debug "Back Navigation Caching" in IE?

I'm seeing an odd bug in IE that I'm not seeing in Chrome. Specifically, this involves some JS code not firing when a (Telerik) wizard is navigated back to it's first step.
When the user clicks their "Previous" button, some data isn't being properly loaded. Hitting F12 and bringing up the developer console has shown me the following Warning:
DOM7011: The code on this page disabled back and forward caching. For more information, see: http://go.microsoft.com/fwlink/?LinkID=291337
Ok, so I go to the link provided and I noticed the documentation states:
In order to be cached, webpages must meet these conditions:
...
- The F12 Developer tools window isn't open
This is a problem, because when I use the navigation buttons within my wizard WHILE the dev window is open, it behaves properly, just as it does in Chrome.
How can I debug my related Javascript so I can figure out what's going on? Also, I understand what caching is but I'm not exactly sure what this is about and I have no idea why Chrome behaves differently. Is there a way that I can force IE to behave like chrome and cut on (or off) whatever features that are causing this issue?
Yuck. Back to old school debugging for you.
Short of putting the whole browser into a Windows debugger, you can pretty much forget about setting breakpoints. All you can do is log.
If you are lucky and your problem isn't too deep, you can use a sprinkling of simple alert() statements to let you know the state of things at various stages in your code. One nice thing is that you can serialize objects now pretty nicely; for example, you can do JSON.stringify(this), which will probably give you a giant output, which you can copy and paste into your IDE and unpack. A major upside to doing this is that the alert will block, so you can take your time studying the output. A major downside to this is that race conditions are now much more likely.
Alternatively, you can add a <textarea> to the page and throw your JSON.stringify(this) results into that. Because this means extra DOM mutations, it also increases the odds of race conditions, but not by much. (If race conditions are a possibility, you can do this:
(function () {
var currentState = JSON.stringify(this);
setTimeout(function () {
document.querySelector('textarea').value = currentState;
}, 1000);
})()
Even though these are now asynchronous, if you use this multiple times in sequence, these will execute in that same sequence (unless you change the timeout period).
If you are doing actual page navigations (and not just changing the URL with pushState()), then actually reading those logs is going to be a problem. The solution is to put the page in a frame and write the content out to a sibling frame. As long as both frames are running on the same domain, you will have no problem pushing the data into the sibling frame. If you can't put them on the same domain, you are kind of screwed.

How can I force a web browser to reload the document when it is viewed by clicking the Back button?

I have seen related questions on StackOverflow, but none of the suggestions are working for me. I need browsers to reload a page from the server every time it is displayed, including if the user got there by pressing the Back button. It seems like browsers obey caching rules for everything except the base document. This happens in current versions of Safari and Firefox (as of Dec 2013), verified by packet capture.
The pages in my application are used to edit a database record. At the top of the source files are a couple lines of PHP to store a lock indicating that the record is being edited. Other users cannot edit the same record while the lock exists. The pages have a window unload handler that uses AJAX in non-async mode to release the lock. (There's more to the locking mechanism, but those are the relevant pieces.) When the user returns to the page via Back button, the server-side code is never executed.
I have tried including a header:
Cache-Control: no-cache, must-revalidate
Safari's inspector shows the header was received and processed, but it still does not re-retrieve the page.
I have tried setting a handler to check whether the page's state was maintained:
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload();
}
};
The if condition never matches: event.persisted is always false.
The annoying part is that this appears to be technically correct. According to the relevant part of the HTML5 spec, since the page registers an unload listener the browser should never try to maintain page state. And it doesn't! When the user presses the back button, the browser is "replaying" the entire page load sequence, including the ready event. It repeats any AJAX calls where the prior results were not cached. The only thing it refuses to actually reload from the server is the main document itself.
How do I get it to reload the main document?
Quick answer:
No you can't... the Back button is even more aggressive and different of a cache than the others. Some insight:
Why is Google Chrome going to the server on pushState?
https://github.com/nickhsharp/prefetchNightmare
That said... a GET request (the browser loading it) shouldn't "do" anything to the server... if anything you should do that lock setting part via an AJAX on the page start... the inverse of how you remove it using AJAX on the done part.
The browsers are pretty clear on their reasons for the crazy BACK/FORWARD caches and you're not going to be able to force their hands on this one.
Adding this code to my HTML works just fine for me:
<input id="isOld" type="hidden" />
<script>
onload = function () {
var el = document.getElementById('isOld');
if (el.value) {
el.value = '';
location.reload();
}
el.value = true;
};
</script>
The code assigns a value to the hidden input that will remain after the back button is clicked in which case the page is force refreshed.
And here's a stripped down version of the above:
<input id="isOld" type="hidden" />
<script>
setTimeout(function () {
var el = document.getElementById('alwaysFetch');
el.value = el.value ? location.reload() : true;
}, 0);
</script>
This time we no longer rely on the onload event which might conflict with other code elsewhere.
There is HTML5 has History API https://developer.mozilla.org/en-US/docs/Web/API/History
Perhaps you may try that.
Nick Sharp's answer is correct - you are trying to find a solution to a problem you've created by the way you've built the application. There are some potential solutions: Optimistic locking mostly works - but bind the session id not the user id. Alternatively you could rebuild it modelling it along the lines of a thick client application - where all the interaction is mediated by javascript within a single HTML page load.

javascript window.onbeforeunload not working correctly

UPDATE: there is something going on with the page I am trying to have the onbeforeunload work on. I set it up in the layout and it pops up for every page besides that one... So there has to be some broken javascript, or a javascript file that redefines onbeforeunload. Since it can only be defined once
I am working on a Rails project and I am setting up a pop up to alert the user that their data will be lost if they leave the page without saving. So I am using window.onbeforeunload
I set it up on one page by adding this script code to the top of the view file
var saving = false;
window.onbeforeunload = function(){
if(!saving)
return 'Are you sure you don\'t want to save before you leave?';
};
where saving is set to true if the user hits the save button, which redirects them to a separate page.
The problem is coming up when I try to set up the EXACT same thing on a separate view file, that also needs the same functionality.
Except when I drop the code above into the file no pop up is given, at all... at any point. So then I looked around at other available options to set up the onbeforeunload function.
So I currently have it set up as:
var saving = false;
window.onbeforeunload = displayConfirm();
function displayConfirm(){
if(!saving){
if(confirm('If you leave without saving, your changes will be lost. Press cancel to go back to save')){
confirmExit();
}
}
}
on the second page. My issue is the pop up here doesn't work the same as the first implementation. Even weirder, the pop up shows up on window load... NOT before window unload.
I have been looking around and messing around with this for a few hours now. I am starting to get really irritated since this should have been an easy addition. Seeing as how it is already set up on a separate page, and working correctly. Any insight onto what maybe going wrong, or if I am making a stupid mistake, would be greatly appreciated.
Thanks,
-Alan
1) window.onbeforeunload = displayConfirm(); -- you're firing the function, instead of assigning it
2) onbeforeunload is great, but it's unsupported in a lot of ways. Some browsers don't even have it, period (all but the most recent Opera, for example), so if you're doing this for a wide audience, or you need it to work 100% cross-browser, onbeforeunload is sadly not enough on its own.
Try with
window.onbeforeunload = displayConfirm;
You are actually calling the function right away and assigning the return value of displayConfirm() to window.onbeforeunload.
Update
But you are limited to exactly one return statement in your onbeforeunload-function, see here. So calling "confirm" or some other custom function does not work.
Recently i was working on a project using this event, so i did do some search on the net.
There are few thing need to be taken into consideration when using the onbeforeunload event.
It is not supported by all browser. Opera, especially older version.
Some support it partially, such as not firing when refresh button is pressed.
Using this event will cause the page will not be cached.
Here is an article that is more thorough about the onbeforeonload event by Patrick Hunlock.

jquery DOM manipulation very slow in IE8 especially addClass and removeClass

I am facing a very strange issue. I have tabs and subtabs in my html and when i click on a tab/subtab 'activeContent' class is placed on it. if i click on another tab/subtab the 'activeContent' class is removed from the previous tab/subtab and placed on the current one. While this scenario works fine when i keep clicking on multiple tabs/subtabs. But in IE8 its very slow. Especially when i hit the back button, the content from the previous subtab is loaded but the active subtab takes a lot of time to change its class. The effect of it is that while that while the content if of some other tab while the active subtab is still the previuos one.
I have even tried to first change the tab/subtab class, something like
$(currentTab.node).removeClass('activeContent');
$(tab.node).addClass('activeContent');
and then used a seTimeout , something like after the above code gets executed.
setTimeout(fuunction(){
//load ajax content
}, 800);
Even then the tabs/subtabs takes a lot of time to change its class.
Is this a IE8 or i might i have to optimize my code. I am not sure. Everything works fine in all other browsers including IE6. Is it has something to do with the back button in IE8?
Are you calling this code when you hit the back button? Most likely the back button is causing a page refresh, and you are waiting for the whole page to reload. IE8 is probably just making this behavior more obvious, because it is handing the caching of page content a little differently.
I have an alternative solution for you. Is this a click event on an anchor tag? I have noticed that it takes an exorbitant amount of time for IE to cancel the default action on an anchor tag that has a href property. Especially in IE8.
Here is an example function from my site:
function SwapLinks() { // This allows our pages to degrade gracefully. But hrefs are slow. So, if JS is enabled remove the href!
$(".playerLink").each(function (index) {
var link = $(this).attr("href");
if (link != undefined && link != null && link != "") {
$(this).removeAttr("href");
$(this).attr("link", ""); // This little number makes IE6/IE7 happy.
$(this).attr("link", link);
}
});
Then you would add a click event on (".playerLink") that handles the Ajax updating.
There was no problem with my code actually. I tested on a friends machine and it was working fine. Then i reset IE8 and everything started to work fine. I am not sure why IE8 was behaving in that way. It happened earlier also, I had to reset IE8 because it was not recognizing the app running on jboss server on my local machine by doing this http://my-pc:8080/myapp/mypage.html BUT rather i had to do http://167.232.23.12/myapp/mypage.html and then it would display evrything. So when i reset the browser , i could run my app through
http://my-pc:8080/ .
I had this problem too, and it turned out it was because I was forgetting to return false; from the click() event. (I imagine e.preventDefault() would work, too.)
I'd been using a link like <a href="#"> for my tabs since it doesn't really navigate anywhere, but IE seem to be "trying" to navigate and taking time to do so, so returning false prevents the navigation for real. (And is probably a best practice, and let's me put in "real" links to fall back to which is probably also a best practice.)
It seems especially a problem when I've loaded the page with a file:// URL on my development machine (as opposed to deploying it to a server and accessing it in the regular way via HTTP).
(Thanks to Jeff Davis and kd44 whose answers above put me on the right track.)

Is monitoring location.hash a solution for history in XHR apps?

As is well known, in XHR (aka AJAX) web applications no history for your app is build and clicking the refresh button often moves the user out of his/her current activity. I stumbled upon location.hash (e.g. http://anywhere/index.html#somehashvalue) to circumvent the refresh problem (use location.hash to inform your app of it's current state and use a page load handler to reset that state). It's really nice and simple.
This brought me to thinking about using location.hash to track the history of my app. I don't want to use existing libraries, because they use iframes etc. So here's my nickel and dime: when the application page loads I start this:
setInterval(
function(){
if (location.hash !== appCache.currentHash) {
appCache.currentHash = location.hash;
appCache.history.push(location.hash);
/* ... [load state using the hash value] ... */
return true;
}
return false;
}, 250
);
(appCache is a predefined object containing application variables) The idea is to trigger every action in the application from the hash value. In decent browsers a hash value change adds an entry to the history, in IE (<= 7) it doesn't. In all browsers, navigating back or forward to a page with another hash value doesn't trigger a page refresh. That's where the intervalled function takes over. With the function everytime the hash value change is detected (programmatically, or by clicking back or forward) the app can take appropriate action. The application can keep track of it's own history and I should be able to present history buttons in the application (especially for IE users).
As far as I can tell this works cross browser and there's no cost in terms of memory or processor resources. So my question is: would this be a viable solution to manage the history in XHR-apps? What are the pros and cons?
Update: because I use my homebrew framework, I didn't want to use one of the existing frameworks. To be able to use location.hash in IE and having it in it's history too, I created a simple script (yes, it's needs an iframe) which may be of use to you. I published it on my site, feel free to use/modify/critizise it.
There are 3 issues that tend to get munged together by most solutions:
back button
bookmarkability
refresh button
The window.location.hash based solutions can solve all three for most cases: the value in the hash maps to a state of the application/webpage, so a user can press one of "back"/"forward"/"refresh" and jump to the state now in the hash. They can also bookmark because the value in the address bar has changed. (Note that a hidden iframe is needed for IE related to the hash not affecting the browser's history).
I just wanted to note however that an iframe only solution can be used without monitoring window.location.hash for a very effective solution too.
Google maps is a great example of this. The state captured for each user action is way too large to be placed into window.location.hash (map centroid, search results, satellite vs map view, info windows, etc). So they save state into a form embedded in a hidden iframe. Incidentally this solves the [soft] "refresh" issue too. They solve bookmarkability separately via a "Link to this page" button.
I just thought it's worthing knowing/separating the problem domains you are thinking about.
I think you'll have a tricky time knowing if a user went forward or back.
Say the url starts /myapp#page1 so you start tracking states.
Then the user does something to make the url /myapp#page2
Then the user does something to make the url /myapp#page1 again. Now their history is ambiguous and you won't know what to remove or not.
The history frameworks use iframes to get around the browser inconsistencies you mentioned. You only need to use iframes in the browsers that need them.
Another con is that users will always go for their browsers back button before they will go for your custom back button. I have a feeling the delay on reading the history every 250ms will be noticeable too. Maybe you can do the interval even tighter, but then I don't know if that'll make things perform badly.
I've used yui's history manager, and although it doesn't work perfectly all the time in all browsers (especially ie6), it's been used by a lot of users and developers. The pattern they use is pretty flexible too.
All that stuff is important for supporting the full range of browsers, but hopefully the need for it will go away. IE8 and FF3.6 both introduced support for onhashchange. I imagine that others will follow suit. It would be a good idea to check for the availability of this functionality before using timeouts or iframes, as it is really the nicest solution currently out there - and it even works in IE!

Categories

Resources