Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support IE<10 #14

Closed
cmather opened this issue Jul 27, 2013 · 31 comments
Closed

Support IE<10 #14

cmather opened this issue Jul 27, 2013 · 31 comments

Comments

@cmather
Copy link
Contributor

cmather commented Jul 27, 2013

This includes support for browsers that don't support pushState (i.e. IE<10).

It includes researching whether we should roll our own solution or use a library like history.js. The integration point would be with our Location class location in lib/client/location.js

Ideally, the user shouldn't need to think about this at all, other than possibly providing configuration options to the router. Asking the user to include additional packages to make this work shouldn't be required in my opinion. However, if/when Meteor implements conditional file loading for packages (e.g. based on user-agent) we could dynamically send down the right javascript.

@cmather
Copy link
Contributor Author

cmather commented Jul 27, 2013

See: #3

@tmeasday
Copy link
Contributor

Here's how I see the options:

Library

  1. Use a polyfill like the HTML5-History-API plugin that we've used in the past.
    • Obviously the integration part is simple
    • I fear the polyfill a) code-heavy and b) potentially resource intensive.
  2. Use a library like history.js to extend location.js
    • History.js is much more widely used.
    • Integration is a bit more involved.
    • Same problem with a lot of extra JS code.
  3. Write our own extension to location.js
    • I think there is a lot of edge cases with this stuff -- thus the libraries are pretty big.
    • We don't have to support that many HTML4 browsers however, so..

Integration

Unless we write our own code (which would probably be a bad idea), it's a lot of extra JS code in the router that a portion of users won't need.

That's why I made a separate package and didn't directly rely on it. Here are some options on how to do it:

  1. Use optional dependencies (which is in linker) to say "if this package is added, use it". It would require uses to mrt add HTML5-History-API or the like.
  2. Conditional file loading based on package options [NOT supported by meteor]. I guess this would be better than the above but only marginally IMO.
  3. Conditional file loading based on browser [NOT supported by meteor]. This would be the ideal solution but I wouldn't hold my breath.

What do you think?

@cmather
Copy link
Contributor Author

cmather commented Jul 28, 2013

That seems reasonable. I Need to look more closely at what htm5 history API package is doing. I'm mostly worried about supporting IE 8-9 but not every edge case. But I don't know if IE8-9 is the the primary case, or if there are many others.

Sent from my iPhone

On Jul 27, 2013, at 11:34 PM, Tom Coleman notifications@github.com wrote:

Here's how I see the options:

Library

Use a polyfill like the HTML5-History-API plugin that we've used in the past.

Obviously the integration part is simple
I fear the polyfill a) code-heavy and b) potentially resource intensive.
Use a library like history.js to extend location.js

History.js is much more widely used.
Integration is a bit more involved.
Same problem with a lot of extra JS code.
Write our own extension to location.js

I think there is a lot of edge cases with this stuff -- thus the libraries are pretty big.
We don't have to support that many HTML4 browsers however, so..
Integration

Unless we write our own code (which would probably be a bad idea), it's a lot of extra JS code in the router that a portion of users won't need.

That's why I made a separate package and didn't directly rely on it. Here are some options on how to do it:

Use optional dependencies (which is in linker) to say "if this package is added, use it". It would require uses to mrt add HTML5-History-API or the like.

Conditional file loading based on package options [NOT supported by meteor]. I guess this would be better than the above but only marginally IMO.

Conditional file loading based on browser [NOT supported by meteor]. This would be the ideal solution but I wouldn't hold my breath.

What do you think?


Reply to this email directly or view it on GitHub.

@krstffr
Copy link

krstffr commented Aug 22, 2013

Any news on this or should I keep using the meteor-router for now? Cheers!

@cmather
Copy link
Contributor Author

cmather commented Aug 22, 2013

It may be a little while. Still thinking it through. Have some additional thoughts that ill post here when I get a moment.

Sent from my iPad

On Aug 22, 2013, at 2:19 AM, Kristoffer Klintberg notifications@github.com wrote:

Any news on this or should I keep using the meteor-router for now? Cheers!


Reply to this email directly or view it on GitHub.

@krstffr
Copy link

krstffr commented Aug 22, 2013

I ported my app to the meteor-router, was surprisingly easy actually.

@dweldon
Copy link

dweldon commented Aug 27, 2013

@cmather I really want to switch edthena over to iron-router, but it's a hard sell if it won't work for IE < 10. If I could vote on a feature, I'd go all in on this one.

@krstffr
Copy link

krstffr commented Aug 27, 2013

Me too, as 98 % of my projects needs to support IE. 

Sent from phone

On Tue, Aug 27, 2013 at 11:30 PM, David Weldon notifications@github.com
wrote:

@cmather I really want to switch edthena over to iron-router, but it's a hard sell if it won't work for IE < 10. If I could vote on a feature, I'd go all in on this one.

Reply to this email directly or view it on GitHub:
#14 (comment)

@ryw
Copy link
Contributor

ryw commented Aug 29, 2013

Just launched an app to production today using Iron Router, and discovering that IE8+9 aren't supported :(

Is there a workaround (ugly is OK) to this issue?

I tried adding the following meteorite packages but didn't help, which I think are fixes for meteor-router...

  • HTML5-History-API
  • page-js-ie-support

@ryw
Copy link
Contributor

ryw commented Aug 29, 2013

Just re-read this issue, and realized this issue seems to be centered around hashbang URLs.

However, I'm getting this error with the root URL in IE8+9 @ newly deployed site http://www.shinglecentral.com/

object doesn't support this property or method 'replaceState'

Something I'm doing wrong, or is it related to this issue?

@cmather
Copy link
Contributor Author

cmather commented Aug 29, 2013

Hey @ryw, I'm sorry that happened to you. This should be at the top of the readme file. I'll be doing an update either tonight or tomorrow.

The issue with IE<10 is that it doesn't support pushState or replaceState. There is no way to change the url without making a server request. So the typical workaround is to use a hashbang at the end of the url which doesn't cause a server request. It stays client side.

If you want to support both: browsers that support pushState, and browsers that don't, you need a library or wrapper. That wrapper says, "If the browser doesn't have a pushState method, fake one, and use hashbangs for the url paths". But this presents its own set of problems that I'm thinking through.

As a possible short term fix, you could fork the project, and add History.js (https://github.com/browserstate/history.js/). The work involved is to use History.js in this file: https://github.com/EventedMind/iron-router/blob/master/lib/client/location.js. So instead of just calling history.pushState and history.replaceState, you call History.pushState (double check History docs).

But I need to see what happens if someone goes directly to a url like: /posts/5.

Another idea is to not try to wrap pushState, but instead, just do a server request if pushState isn't supported. So there's a little bit of thinking required here before we just throw in a History.js like solution to the main project.

@tmeasday
Copy link
Contributor

Oh, and you'll also probably need to change the click handler support to work properly on old broken browsers.

Here's the equivalent change to the old page.js library that we used to use that made it work with HTML5-History-API (a slightly different polyfill to History.js) visionmedia/page.js#48

@cmather cmather mentioned this issue Aug 30, 2013
@sterlingwes
Copy link

@ryw I've hacked together a solution that works quite well in IE8+, haven't tested elsewhere. Uses only HTML5-History-API. Check my fork https://github.com/sterlingwes/iron-router

@cmather
Copy link
Contributor Author

cmather commented Aug 31, 2013

I spent some time on this today. It looks like there are a couple of options, each with pros and cons. I'll lay out the issues and solutions as I understand them.

Issues

  • Normalize event handling for onpopstate window event for IE and w3c (easily accomplished with jQuery; UniversalEventListener doesn't seem to work with window)
  • HTML4 browsers (e.g. IE<10) don't support history.pushState and history.replaceState
  • HTML4 browsers don't fire the onpopstate event
  • HTML5 history API may be different across some browsers (I'm still researching)
    • Differences in handling state
    • Differences in location.pathname
    • others?

Solutions and Questions

  • Normalize event handling for popstate (for html5 browsers)
  • For HTML4 browsers, map pushState and replaceState to a location change and force a server request
    • This may be the easiest solution. But one problem is performance. Every link will cause a server request and a complete reloading of the Meteor application.
    • This might be partially mitigated once Meteor implements server side rendering (with the ability to cache)
    • But there's still the issue of data subscriptions. If someone navigates to /posts/5, you might want to wait on the posts data subscription to complete before rendering the page (to determine if the post even exists). Now, for example, every time I click 'next' on eventedmind.com, I have to wait for all the posts to load, making the page loading time seem tediously slow.
  • Use hashes or hash bangs for HTML4 browsers.
    • I'm still figuring out the difference between History.js and HTML5-History-API
    • But essentially, they seem to detect HTML4 browsers and allow you to mimic pushState and popState
    • What happens when you start off at route /posts/ and then you navigate to the postShow path? Is the path in the href: /posts#5? But if you navigate to /posts/5 directly it gives you the same result? Maybe this is okay, but seems strange.

cc @gschmidt

@cmather
Copy link
Contributor Author

cmather commented Aug 31, 2013

@sterlingwes, I just saw your post and checked out your code. I need to take a closer look at HTML5-History-API but it looks like your changes to iron-router are on the right path for the third solution above. What about the pathFor and urlFor helpers. I may have missed it, but it looks like your code handles it properly if the hash already exists, but if I click a new link it causes a page load.

@sterlingwes
Copy link

@cmather I don't currently use the helpers in my project so I likely neglected that, will revisit. I have to take some time for a more thorough look (tests etc.), but if you decide to go this route I'd be happy to contribute. Thanks for all your work on this.

@cmather
Copy link
Contributor Author

cmather commented Aug 31, 2013

That might just be a good plan @sterlingwes. I would like to gain a better understanding of the differences between HTML5-History-API and History.js.

Also giving thought to whether this is something that should just be part of the iron-router package at some point; or if it's such an edge case that it should be an opt-in with a separate package.

@cmather
Copy link
Contributor Author

cmather commented Aug 31, 2013

Oops, actually I got a little confused here myself. Too much to keep in one mental model :-). The onClick handler of client_router does intercept the click so it should be fine to do the hash stuff here (or in go as you have it). But then we still have the issue of when a user navigates directly to a path like posts/5 vs posts#5. @sterlingwes, I'll take another closer look at your changes. Will resume over the weekend.

@tmeasday
Copy link
Contributor

Hey, just to weigh in here a little:

  1. The difference between HHA and history.js is that history.js requires you to use their api, where as HHA is a polyfill - that is you continue to use the standard html5 Apis.
  2. However history.js is much more mature and well used, so that's a factor.
  3. Whichever we use my advice would be make the router core agnostic to what's going on and put all the logic in Location. So all urls would continue to be written with slashes and wherever a user browses to, location figures it out and tells the router a normalised thing.

El 31/08/2013, a las 11:22 AM, Chris Mather notifications@github.com escribió:

Oops, actually I got a little confused here myself. Too much to keep in one mental model :-). The onClick handler of client_router does intercept the click so it should be fine to do the hash stuff here. But then we still have the issue of when a user navigates directly to a path like posts/5. @sterlingwes, I'll take another closer look at your changes.


Reply to this email directly or view it on GitHub.

@ryw
Copy link
Contributor

ryw commented Aug 31, 2013

@sterlingwes I tried popping your fork into my project, but still looks like IE not loading in production :( http://www.shinglecentral.com — I downloaded your code, put it in a packages/iron-router directory, and removed the meterorite version of iron-router... Any way for you to tell if your fork seems to be deployed @ my URL?

@sterlingwes
Copy link

It's there, but you have other IE compatibility issues with your code. For one, you're using the reserved name class as an object key. IE doesn't like that. Hit F12 then reload for the line #. In your compiled code it's hard to tell but somewhere near where you're concatenating form-control to a string.

@ryw
Copy link
Contributor

ryw commented Aug 31, 2013

@ryw
Copy link
Contributor

ryw commented Sep 1, 2013

@sterlingwes thanks Wes — we fixed autoform, sorry for hijacking this issue — back to solving issues in iron-router :)

@manuel-schoebel
Copy link
Contributor

@sterlingwes I found a little issue with your solution.

For example you have a named route 'creditsBuy' with the path '/credits' the following would happen in this snippet (client_router.js):

onClick: function (event) {
    var el = event.currentTarget
      , href
      , path;

    if (el.nodeName === 'A') {
      href = el.href;
      path = el.pathname + el.search;

It seems that in IE9 (i guess in 8 too) el.pathname is 'credits' and not '/credits'. This leads to the error 'No route found named credits', because in the "go()" function the router thinks its a name but it should be a path.

I fixed it by adding a '/' if there is none:

if (el.nodeName === 'A') {
      href = el.href;
      path = el.pathname + el.search;

      if(path === '' || path.charAt(0) !== '/'){
        path = '/' + path;
      }

The pathFor Helpers now also seem to work for me.

https://github.com/DerMambo/meteor-iron-router/blob/master/lib/client/client_router.js

@sterlingwes
Copy link

@DerMambo thanks for the heads-up. That also helps me revert some changes I made to go() in search of that issue, so you may want to pull that down. Cheers

@manuel-schoebel
Copy link
Contributor

And i found another thing, that might be to consider.

I have a twitter bootstrap modal. It is used by putting the id of the modal into the a-tags href.

This a-tag opens a modal for example:

a href="#modalId" data-toggle="modal">Open Modal

Clicking this makes the IronRouter want to show the modalId template and this of course does not exist. I made a quite hacky workaround by adding a new tag to a elements.

a href="#modalId" data-toggle="modal" data-iron-router-ignore="true">Open Modal

And i check for this in the onClick Event. This really is not very nice, but okay for me for a while ;)

I am not sure but maybe you would have to call e.preventDefault() or stopPropagation in the bootstrap script?!

@cmather
Copy link
Contributor Author

cmather commented Sep 10, 2013

On the dev branch, the Router will no longer break with IE8-9. Please clone the repo and try it out. However, the way it is implemented is that if history.pushState is not supported, the browser makes a server request. So it will work, but there's a performance penalty. This might be mitigated somewhat with server side rendering and prioritized subscription management from Meteor.

I'll come back later and outline some of the design thinking. Going to close this issue but would like to continue discussion.

@cmather cmather closed this as completed Sep 10, 2013
patreeceeo pushed a commit to patreeceeo/iron-router that referenced this issue Nov 30, 2013
@DominikGuzei
Copy link

I could make it work in combination with Html5-History-API package by patching the normalizePath method of the route:

Route.prototype.normalizePath = function(path) {

  var origin = Meteor.absoluteUrl();

  path = path.replace(origin, '');

  var queryStringIndex = path.indexOf('?');
  path = ~queryStringIndex ? path.slice(0, queryStringIndex) : path;

  //var hashIndex = path.indexOf('#');
  //path = ~hashIndex ? path.slice(0, hashIndex) : path;

  if (path.charAt(0) !== '/')
    path = '/' + path;

  return path;

}

The problem were the two lines I commented out. These modify the path if there is a hash present. It effectively destroys any support for older browsers where Html5-History-API uses hashes instead of slashes. It's a pretty simple fix, and maybe someone could explain why these two lines are in any way important, they don't seem really helpful to me anyway?

By the way, I want to say its really awesome that one can override any iron-router API methods, this makes it so much easier to work with than e.g. angular.js where the core is completely fenced off.

@tgoorden
Copy link

Allright. I've run into the same challenge as everyone here: getting stuff to run on IE8. With the most recent version of Iron Router, combined with HTML5-History-API, everything seems to work fine, except...
If you use something like

<a href="{{pathFor 'home'}}>Home</a>

on Internet Explorer 8 (haven't tried it on 9) this will trigger a full page refresh.

However, if you intercept this event and use the Router directly, the page refresh can be avoided! The code would look something like this (sorry for the Coffeescript syntax here):

Template.navigation.events
   "click a": (event,template)->
      event.preventDefault()
      Router.go event.currentTarget.href
      return

That works rather nicely. I have not tested what happens if you start including variables, but I suspect this can easily work as well.

What I find odd is that I kind of thought Iron Router was intercepting <a href="... stuff already? For some reason, that doesn't seem to work on IE8, even though the code seems trivial.

@suprsidr
Copy link

I see this thread is old, but I ran into this last night. I don't care about IE8, but support for IE9 is still important to our company. Though IE<10 is only responsible for ~3% of total site traffic/mo that is ~$50,000 in revenue. I have seen many responses with partial fixes, and even some claiming this was fixed in v0.6. But I'm on iron:router@1.0.7 and most pages of my app are blank and all urls have hash bangs in them. @cmather is there some direction you can point me in?
Errors:
Exception in setTimeout callback:Error: Syntax error, unrecognized expression: #!story/qZjvd5bRpHcn9EvKo
Exception from Tracker recompute function:
undefined
Exception in setTimeout callback:Error: Syntax error, unrecognized expression: #!search

@suprsidr
Copy link

So I thought I'd try a simple old polyfill https://cdn.polyfill.io/v1/docs/
And it works.
Please disregard my previous plea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants