Is History API broken on iOS? (Location bar doesn't update on pushState) - ios

Filing this under the either the I Can't Believe No One Noticed This Before or the I Must Be Missing Something categories:
It appears that if you do a simple window.history.pushState on iOS, the location bar doesn't update unless it is in response to a user gesture. The state itself does get pushed (as you can see by hitting the back button button).
Here's is the tiniest test-case I could come up with recreate the issue:
http://thelink.is/history-api-ios-bug
On a desktop browser that supports the History API, you should see the URL in the location bar change to /0, /1, etc., every second. On iOS – tested with iPhone (running iOS 4.3) and iPad (running iOS 4.3.3) – the location bar doesn't update but hitting the back button will take you the correct previous location (which will 404 on the test-case since there's no back-end logic to handle those URLs).
Thoughts? Workarounds? A shoulder to cry on and hugs?
UPDATE: this issue was fixed in iOS 5.

So the bottom line is that iOS has added its own security around the history API, meaning that you can't use script to change the url. Only a user action can allow the history API to change the url - i.e. a click - as per Aral's example.
The workaround is to uses a hash (aka fragment identifier) on the url.
Instead of the history.pushState we'll just change the location:
var i = 0;
var locationUpdateInterval = setInterval(function(){
window.location.hash = i;
i++;
}, 1000);
To capture the event either when something changes the that location in the iOS app or if they have permalink to a particular page/panel in your app:
// named function on purpose for later
function hashchange() {
var pageId = location.hash.substr(1); // drop the # symbol
// do something with pageId
}
window.onhashchange = hashchange;
// onload - if there's a hash on the url, try to do something with it
if (location.hash) hashchange();
It's pretty poor that we can't use the pushState/popState on iOS, but it's the same security as not being able to trigger fullscreen video unless the user initiates the action, which is the same as downloading video or audio content on iOS - you can't script it, the user must start it (somehow or another).
Just as a note about Android - the problems are pretty similar, so this (should) also work as a workaround for Android.
If you want desktop support, most browsers support onhashchange but, yep, you guessed, IE is lacking behind - so you can polyfill that bad boy in (though requires jQuery...): http://benalman.com/projects/jquery-hashchange-plugin/
Hope that helps.

Works fine for me when using: https://github.com/browserstate/history.js - this also fixes many other cross browser bugs with the HTML5 History API.
As of v1.7, here are the bugs it solves:
History.js solves the following browser bugs:
HTML5 Browsers
Chrome 8 sometimes does not contain the correct state data when traversing back to the initial state
Safari 5, Safari iOS 4 and Firefox 3 and 4 do not fire the onhashchange event when the page is loaded with a hash
Safari 5 and Safari iOS 4 do not fire the onpopstate event when the hash has changed unlike the other browsers
Safari 5 and Safari iOS 4 fail to return to the correct state once a hash is replaced by a replaceState call / bug report
Safari 5 and Safari iOS 4 sometimes fail to apply the state change under busy conditions / bug report
Google Chrome 8,9,10 and Firefox 4 prior to the RC will always fire onpopstate once the page has loaded / change recommendation
Safari iOS 4.0, 4.1, 4.2 have a working HTML5 History API - although the actual back buttons of the browsers do not work, therefore we treat them as HTML4 browsers
None of the HTML5 browsers actually utilise the title argument to the pushState and replaceState calls
HTML4 Browsers
Old browsers like MSIE 6,7 and Firefox 2 do not have a onhashchange event
MSIE 6 and 7 sometimes do not apply a hash even it was told to (requiring a second call to the apply function)
Non-Opera HTML4 browsers sometimes do not apply the hash when the hash is not urlencoded
All Browsers
State data and titles do not persist once the site is left and then returned (includes page refreshes)
State titles are never applied to the document.title

(Update: Just saw that Remy answered too – read his in-depth answer, above, instead.)
Remy provided a workaround for this issue on Twitter.
Basically, if you change the location.hash, the address in the location bar updates. However, this does create a separate entry in the history (which doesn't work for what I'm trying to achieve). The workaround I'm implementing is to use hash-bang URLs for iOS and regular ones for other platforms until the iOS bug is fixed. This is definitely not ideal and I hope that Mobile Safari on iOS will start behaving like Chrome, Firefox and Safari on desktop.

Here's what I found:
When the pushed location contains a hash symbol, the adressbar will be updated.
So this will work:
window.history.pushState(data, title, 'a/new/url#');
But the window.location object will not be updated so you need to save the pushed url into a variable and use that instead of window.location if you need the pushed location.
Tested on Safari for Android.

I found a hack that kinda works. It turns out that if you change the hash right after history.pushState the location bar gets updated. Like:
window.history.pushState(data, title, 'a/new/url');
window.location.hash = 'new';
changes the location bar to http://example.com/a/new/url#new. Which introduces another problem because the hash becomes it's own history entry. So you'll need to listen to onHashChange anyway.
It's a bit complicated, but there are some people who really, really hate hashbang urls and are very vocal about it. So it's worth it.

Related

VueJs Nuxt PWA not rendering on iphone

My vuejs nuxt portfolio(https://gunjankadu.com/)is not opening on ios phones but working flawlessly on all other devices. On IOS devices only a red spinner is shown and nothing more.
What can be done?
A script "tag.js" took time to load, which I have currently disabled but still not working.
I had a look on your application and found out two problems.
First was about regular expressions, It seems Safari doesn’t support look behind yet. One alternative would be to put the / that comes before in a non-captured group, and then extract only the first group (the content after the / and before the #).
/(?:\/)([^#]+)(?=#*)/
And second is about The minimal-ui viewport property that you can find out more here
Look at all your js/vue files especially in store. And see if any variable used is not imported. Nuxt in SPA mode doesn't detect those kind of errors and surprisingly the UI gets stuck on loading only on iOS. Very weird but scratched my brain over it for a day.

Debug slow angular-ui-router state change on Mobile Safari

I'm writing an angular app, using angular-ui-router to manage states / routing.
On desktop browsers (Chrome / Safari) this is working fine. However, on Mobile Safari on IOS 6 on iPhone 4 (and to a lesser extend on IOS 7 on iPhone 5) changing state, via $state.go, can take anything up to 2 seconds.
I'm using ngTouch, so I don't think that it's the 300ms that the native click event takes to fire. ngClick attributes that don't call $state.go now seem to work pretty much instantaneously.
How can I debug this to find where the time is being spent?
ngTouch doesn't work with the ui-sref directive. We used fastclick.js to handle the click behavior, and removed ngTouch. The issue is that the directives step on each others events, and are in fact incompatible. You can see this by reading the implementation of both directives.
Use console.time to print out in the Mobile Safari Console,
https://developer.chrome.com/devtools/docs/console-api#consoletimelabel
...in association with the ui-router listeners :
$stateChangeStart
$stateChangeSuccess
$viewContentLoading
$viewContentLoaded
/* EXAMPLE - INCREMENT THE TIMER
FOR EACH ONE OF THE LISTENERS, TO DETECT THE BOOTLENECK */
// START TIMING NOW
console.time('state_transition');
/*
http://devin-clark.com/what-you-might-not-know-about-chrome-devtools/
*/
$rootScope.$on('$stateChangeSuccess',
function(event, toState, toParams, fromState, fromParams){
console.timeStamp('state_transition');
});
// STOP TIMER
console.timeEnd('state_transition');
http://www.ng-newsletter.com/posts/angular-ui-router.html
Who knows, maybe for improving ui-router's reactivity on mobile devices, FutureState from UI-router's extras could be something worth to explore :
http://christopherthielen.github.io/ui-router-extras/#/future
...but the only way to be sure is to understand the innards of the mobile browsers and their DOM/JS/GPU's management/computing limitations in respect of desktop browsers.
It could be that ng-animate requires too much of a mobile browser by building the "next" template ahead of its appearance into the viewport. And yet it could be something completely trivial.
Please let us know your progress.

MVC 4 - Upload Doesn't Work in IE When deployed to IIS 7.5

This problem only happens when I deploy this to our live server, it works fine on my development machine in the visual studio web server, and it is only a problem in IE (8 & 9 both)
Posting a jpeg with this form...
<form method="post" id="uploadForm" enctype="multipart/form-data" action="/ImageUpload/UploadImageNew">
<input type="file" onchange="postFile()" name="file"></div>
<input type="submit" value="OK">
</form>
Using this javascript...
function postFile(ctrl) {
document.getElementById('uploadForm').submit();
}
To this controller...
[HttpPost]
public ActionResult UploadImageNew(HttpPostedFileBase file)
{
// Verify that the user selected a file
if (file != null && file.ContentLength > 0)
{
file.SaveAs("AFilename.file");
}
else
{
throw new Exception("File not found")
}
return View("UploadImageNew");
}
Results in file.ContentLength = 0 in IE, but it works fine in FF & Chrome, the machine is on our Intranet if that makes any difference.
Any help greatly appreciated
UPDATE 1:
Weirdly it seems that the problem is intermittent, on Friday my colleague could not upload anything but I could, this morning it is me who can't and my colleague can both using IE.
Everything seems to point to an IIS config problem?
UPDATE 2:
Ok, it looks like my issue is related to expired sessions / security. I am using a Hybrid Authentication Method and it is causing me problems.
The main site uses forms authentication, however I have another site which does windows authentication for me and sets the cookies, disabling this fixed the issue for me.
Not sure why yet but I think this would be better in another question.
I am marking smartcaveman's answer as the right one, because his post lead to me to the right answer/explanation.
Your issue has nothing to do with the IIS. It is broken because you are wiring up using the change event. There are known issues with IE and the change event. A table with the compatibility of different browsers with this event on different HTML elements is available at quirksmode.
Here are a couple of blog articles on the subject:
http://passionatedevelopment.com/blog/2010/03/24/onchange-on-ie-vs-firefox-the-importance-of-cross-browser-test/
http://www.nofluffjuststuff.com/blog/scott_leberknight/2005/04/onclick_versus_onchange_in_ie_and_firefox
The most obvious way that occurs to me to do this is to use the blur and focus methods instead. (Record a value on focus, check it on blur, and if it's different submit).
Update
So, It's still not working? Here's a few other things that are worth looking into.
What happens if you try to get the value from the form field on the client-side, prior to submitting. e.g. function postFile(ctrl) { alert(document.getElementById('uploadForm').value); return false; }. Does it have a value? Also, have you confirmed the feature works on the live environment without the JavaScript? (e.g. just submit the form normally). If it does, then you can be sure the problem is on the front end. If it doesn't then the JS is fine and the problem is on the back-end.
Max Request Length / Max Content Length. In a comment on your post, you said that you have enabled up to 2GB in the maxRequestLength. However, this works a little differently in IIS7 than in IIS6 (or Casini if that was your previous test environment). The details about this are cited in this blog, but in summary you need to make sure that your web.config has the setting in the system.webServer section, and that the value actually represents what you think it does. Details are in the article.
Do you have exception handling and/or logging features in play? Is it possible you are swallowing an exception somewhere that is causing the request to be abandoned? Are there any empty try - catch blocks that might be protecting your view from an underlying error that would otherwise cause the request to fail? If you do have logging enabled, can you isolate a distinction between your attempted usage and your colleague's?
Is there anything different about the file structure on the live site than your development environment? I noticed you have a hard-coded form action /ImageUpload/UploadImageNew target, which could affect the application's ability to match the incoming route.
You said in the comment that the content length is 0, but if the test you used to determine this is what you have in the post, then you may be wrong. Your test is equivalent to file == null || file.ContentLength == 0). There are different implications depending on which is actually the case. If the file is null, then it may related to model binding. If the Content length is 0, then it at least recognized a file is being sent, but something goes wrong when it decides what to do about it. You can check to see if you are actually receiving the file data by looping through the HttpContext.Request.Form collection and writing out the values (That's how we used to do it... in the ASP Classic days). This other post, Valum file upload - Works in Chrome but not IE, Image img = Image.FromStream(Request.InputStream) , says that a user with the same kind of issue found the file content in the Request.Files collection. You won't know for sure until you check. Anyway, if the file data is in one of those places, you know that problem is happening in model binding (and you know the work around until you find the right way to fix it).
I also found something on the Telerik forums that seems to describe something similar that happens with their component (which is really just a wrapper of what you are doing). It points to a possible session time out.
I would suggest going through these items and seeing if any of them reveal more about the problem (at least #1 is sure to).
Last time I have the same problem when I write ajax based UI.I upload the file using the ajax.only Ie create problem for that time.
Not sure but try these link maybe helpful
Ajax file upload not working in IE7
Valum file upload - Works in Chrome but not IE, Image img = Image.FromStream(Request.InputStream)
http://haacked.com/archive/2010/07/16/uploading-files-with-aspnetmvc.aspx
Is you postFile() save file by ajax or form submission ? IE not take the filename (of upload) as chrome and firefox does. When you make upload debug the code and check if they are exist.
The easy solution that worked for me on IE11: Enable Compatibility mode.
For some reason, IE11 was doing two requests to my Upload endpoint... One without the file, that works fine and returns 200 status (but since it doesn't has the file, it's useless for me) and another request with the file... But this second one is always "pending" on the network tab of the Developer Tools inside IE. I couldn't find the reason for this odd behavior. Watching the logs from server I noticed this second request never hit the server.
This however don't solve the problem for everyone since you'll have to ask every user of your system to enable compatibility mode for your website... And it's not a good thing to ask for Internet users.

jQuery mobile.changePage returns to first page after initial changePage

Here's a problem only with web browsers on the computer, in other words it's not a problem on smart phones as an app.
I updated my jquery mobile framework to the latest release, and i'm trying to log into my account which loads other pages, but as soon as it loads the other pages it loads back to the log in screen. My question is, are there any background functions tied to the changePage function that have changed since 1.0b2'ish release which may affect this?
There have been several changes made to the $.mobile.changePage() function including some deprecations that had code removed for the 1.0 release (latest at the time of this writing).
If you go here: http://jquerymobile.com/blog/, and search for "changePage", you will find some interesting information like this:
Removed support for the alpha signature of $.mobile.changePage() in
preparation for 1.0. Folks now how to use the signature that requires
the toPage (url or element) as the first arg, and options object as
the 2nd. See the events API documentation and commit log for more
info.
You could also take a look at the documentation for $.mobile.changePage() and make sure the current documentation fits with how you are currently using the function: http://jquerymobile.com/demos/1.0/docs/api/methods.html
Updated doc link for 1.1.0: http://jquerymobile.com/demos/1.1.0/docs/api/methods.html

Cross browser addons

This has been discussed in a few threads - but none gave any real answers.
I need to develop a very simple browser addon which just has a single button, and can run a javascript function when pressed. It must sit as a toolbar or similair.
Now, is there an easy way to develop once for Chrome, IE, and FF?
Only supporting the latest version of each browser is fine too.
Thanks
If you mean a javascript (not particular to a certain browser e.g showing the history) you can run it like this (pasting it into the url bar):
javascript:alert("Hello!");
You can make a link with the href going to your script, then tell your users to drag the link into their bookmarks bar. E.g
Link text

Resources