enquire.js icon indicating copy to clipboard operation
enquire.js copied to clipboard

Mis-fires in Chrome/Mac and all iOS7 browsers

Open brendanfalkowski opened this issue 11 years ago • 25 comments

This is an issue that affects the native matchMedia, Enquire, and Harvey: https://github.com/harvesthq/harvey

Follow-up from this Twitter conversation: https://twitter.com/Falkowski/status/387294544988889089

See test case: http://cdn.gravitydept.com/matchmedia/

I also setup a test in CodePen, but I'm not sure this is representative since it loads within an iframe even in full-screen mode.

See test-case in CodePen: http://codepen.io/brendanfalkowski/pen/qCauf Full screen: http://codepen.io/brendanfalkowski/full/qCauf


Chrome/Mac has an off-by-one error

Chrome Version 30.0.1599.69

If the browser is 1200px, then resized to 1023px ➔ the (min-width:1024px) query is not unmatched until 1022px.

Opposite: if the browser is 1000px, then resized to 1024px ➔ the (min-width:1024px) query is not matched until 1025px.

This seems to affect matchMedia, Enquire, and Harmony equally in Chrome. Firefox, Safari, and Opera calculate correctly.


iOS 7 orientation change bug

Safari and Chrome for iOS 7 do not fire min-width or max-width queries on orientation change.

In my demo, media queries only fire if the browser is refreshed, but min-width:1024px should fire in iPads on orientation change.

In the CodePen, no media queries fire on refresh so I'm pretty sure the iframe is preventing some behavior.

brendanfalkowski avatar Oct 10 '13 22:10 brendanfalkowski

Also opened a bug in Chromium: https://code.google.com/p/chromium/issues/detail?id=307812&thanks=307812&ts=1381893810

brendanfalkowski avatar Oct 16 '13 03:10 brendanfalkowski

Since these bugs are demonstrable in the matchMedia API, I'd say there's little I can do about it except wait for the bug to be fixed. Do you think I should just close this issue on those grounds?

WickyNilliams avatar Oct 16 '13 09:10 WickyNilliams

Unsure of the issue etiquette, but since the bug is technically affecting it might be useful to keep open if others have the same issue.

brendanfalkowski avatar Oct 16 '13 15:10 brendanfalkowski

Have you tried commenting out the Harvey events? When I did that, both Enquire and matchMedia events get fired on orientation change. Seems to me that Harvey is contributing to or causing the problem.

va7map avatar Oct 16 '13 17:10 va7map

@va7map Thanks! Great idea, that does work as expected. Removing Harvey solved the Chrome off-by-two issue and the iOS7 orientation bugs.

Removing Enquire did not have the same effect, so it appears this is actually a Harvey issue though I'm not sure how it's impacting Enquire and the native matchMedia query. I'll update the other reports and test-case to reflect this.

brendanfalkowski avatar Oct 16 '13 18:10 brendanfalkowski

You're welcome @brendanf. I just happen to be dealing with a similar issue. I'm not using Harvey, so something else must be causing it...

va7map avatar Oct 16 '13 21:10 va7map

I ran into the same issue and I'm not using Harvey either.

sachinjsk avatar Oct 18 '13 20:10 sachinjsk

@va7map and @sachinjsk What other JS is included when you experience the issue? In my reduced case it was jQuery 1.10.1 and Harvey (which was causing the issue). Wondering what other libs are causing conflicts. Might be able to isolate a common component or practice between the offenders.

brendanfalkowski avatar Oct 21 '13 00:10 brendanfalkowski

@sachinjsk can you whip up a little fiddle/codepen to demo the problem (ideally a reduced test case)?

WickyNilliams avatar Oct 24 '13 17:10 WickyNilliams

I was able to isolate the cause of the issue I'm seeing. Basically, if there are similar CSS media queries on the page, the matchMedia listeners won't get called right away, until the browser is further resized by another pixel.

I've filed a bug with WebKit (link), and here's a test page you can try: https://bug-123293-attachments.webkit.org/attachment.cgi?id=215113

va7map avatar Oct 24 '13 22:10 va7map

Here's my test case - http://bit.ly/1cgEkst

That test case works fine if you remove the media query definition from CSS.

I created a codepen (http://codepen.io/sachinjsk/pen/bcqzh) with the exact same code, and that works fine(even with the media query definition in CSS).

sachinjsk avatar Oct 25 '13 02:10 sachinjsk

@sachinjsk If you slowly resize your browser pixel by pixel, and compare the results between Firefox 24 and Chrome 30, you should notice that in Firefox (which isn't affected by this bug), the transition between matched and unmatched happens at the same time the text color changes. But in Chrome 30 (or Safari 6.1) the color changes first. The actual matching or unmatching doesn't happen for another pixel. And that's exactly the same issue in my test case.

By the way, your test case on Dropbox has an error on Firefox 24 and Chrome 30, because they now block insecure content. You can remove "http:" in src attribute of the scripts so it's relative to the protocol.

va7map avatar Oct 25 '13 16:10 va7map

I've simplified @sachinjsk's test case a bit:

http://devrandom.com/test/ios7-matchmedia/

You can see that things are fine if you embed that url inside an iframe:

http://devrandom.com/test/ios7-matchmedia/iframe.html

Don't see how that really helps (just an explanation to why @sachinjsk's codepen worked), but one thing that did help was that I found that triggering a reflow/repaint would fix things:

http://devrandom.com/test/ios7-matchmedia/fixed.html

Not a pretty fix at all (and I don't think it's something I would commit into enquire.js), but for anyone who needs things to work in iOS7, this bit of JavaScript seems to work for me:

(function () {
    if (document.addEventListener) {
        document.addEventListener('DOMContentLoaded', function () {
            var shim = document.createElement('div');
            shim.id = 'ios7-matchMedia-fix';
            document.body.appendChild(shim);

            var timer,
                forceReflow = function () {
                    shim.style.width = (window.innerWidth / 2) + 'px';
                },
                onResize = function () {
                    clearTimeout(timer);
                    timer = setTimeout(forceReflow, 100);
                };

            window.addEventListener('resize', onResize, false);
        });
    }
})();

The width itself doesn't seem to be important (just made it / 2 as to avoid overflow). Probably could optimize the code a little more by only running on iOS user agents and possibly removing the timer since the resize event shouldn't be triggering continuously on iOS like it does in IE.

Edit: Forgot to mention that this looks like an iOS7 bug, not an enquire.js bug.

brewt avatar Oct 29 '13 03:10 brewt

@brewt Your iframe-embedded test case didn't work for me on the desktop (it did work on the iPad though). It behaves the same way as the non-iframe one. This is what I see on Chrome 30 and Safari 6.1:

  • ≤ 768px — matched (red text)
  • 768px -> 769px — matched (black text), which shows that there is a bug
  • 769px -> 770px — un-matched (black text)

I also found that triggering a reflow/repaint fixes the problem.

va7map avatar Oct 29 '13 16:10 va7map

As mentioned in the original report, an iframe test cannot be trusted because the parent page may not be rendered at native size if the "viewport" meta tag isn't set to prevent scaling. Thus some devices will not trigger the issue if the example is narrower than the device's native viewport and scale value. To test accurately, you have to load a static page with the viewport tag set.

@va7map In my project (now Harvey-less) I do have CSS media queries firing on the same query as Enquire, and I'm not experiencing the issue. So that may not be a cause.

brendanfalkowski avatar Oct 29 '13 17:10 brendanfalkowski

For the repaint, could you not just read any property on anything and then write that exact value back? e.g. window.scrollY = window.scrollY? Slightly simpler than creating new nodes etc...

So this has gone back and fore a few times now, and I'm having difficulty keeping tracking of things (could just be me!). Can we summarise everything we know so far e.g. affected browsers, whether it is reproducible with just the underlying matchMedia API etc?

WickyNilliams avatar Oct 29 '13 17:10 WickyNilliams

@WickyNilliams From my testing, Safari started exhibiting the problem in iOS 7 and since 6.1 on OS X, so my guess is any browsers using WebKit 537 or later. It is reproducible with the basic matchMedia API, when there is a similar CSS media query present. The effect is that the matchMedia listener will not be called initially when there is a change to the media query results (e.g. when rotating and iPad from portrait to landscape). On the desktop, resizing the browser window further will fix it.

@brendanf I'd be interested to see the counter-example you mentioned. Can you please post that somewhere for us to look at?

va7map avatar Oct 29 '13 18:10 va7map

@va7map Seems I spoke too soon.

My original test case without media queries which is working properly: http://cdn.gravitydept.com/matchmedia/

When I added a CSS media query at 1024px (change body color), the bug DOES return: http://cdn.gravitydept.com/matchmedia-with-mq/

You can see the CSS media query applies, but the JS matchMedia does not (as the bug was described initially). This demonstrates there is a fault occurring when both JS and CSS media queries are applied.

brendanfalkowski avatar Nov 05 '13 17:11 brendanfalkowski

Quick update for Mac browsers: the bug is still present in Safari 7.0.1, but is absent/fixed in Chrome 32.0.1700.77 (probably Chrome 31 as well).

va7map avatar Jan 15 '14 19:01 va7map

Ran some tests to confirm @va7map's latest updates:

Confirmed working:

  • Chrome for Mac, 32.0.1700.77
  • Firefox for Mac, 26.0

Not working:

  • Chrome for iPad, 31.0.1650.18
  • Safari for Mac, 7.0.2
  • Safari for iPad, iOS 7.0.4

brendanfalkowski avatar Jan 20 '14 01:01 brendanfalkowski

I have also found this issue on iOS7.

@WickyNilliams Unfortunately window.scrollY = window.scrollY and some other forms of forcing a repaint didn't seem to work.

The only ones that worked for me were appending an element as mentioned above and setting the body width:

$('body').width(1).width(''); which I found preferable to appending an extra DOM node.

Further to @brewt's solution you only need to set the timer to 0 so it is deferred to the end of the call stack.

timer = setTimeout(forceReflow, 0);

If anyone has a tidier solution, I'd love to know.

mderrick avatar Mar 27 '14 15:03 mderrick

Guys, apologies for being elusive... I have a load of time this week to investigate this.

Can someone confirm whether the big was ever present on iPhone or just iPad? I now have an iPhone for testing, and can probably grab an iPad this week if need be.

WickyNilliams avatar May 07 '14 17:05 WickyNilliams

Don't think I tested on iPhone because the breakpoint I needed only affected iPad-sized devices. The tests I put together should still be accessible, but you could pretty quickly modify the breakpoint to suit whatever size/generation the iPhone you have is. (They were off-by-one errors, so the exact dimension matters more than setting an obvious gap between orientations).

brendanfalkowski avatar May 07 '14 18:05 brendanfalkowski

The issue is present in Safari on Mac OS X as well, and it doesn't seem to depend on a specific resolution. Based on this, I'd say it affects the iPhone too.

va7map avatar May 12 '14 20:05 va7map

I was not able to reproduce this in Safari 12 on macOS Sierra using the test page @va7map attached to the bug report.

ryanfitzer avatar Nov 29 '18 00:11 ryanfitzer