Problem:
I need a client-side Javascript solution (jQuery is fine) where an event in one browser window/tab can be broadcast to other windows/tabs on the same domain. For example, if a cart is updated in tab A, tabs B and C get notified so we can update some info on the page, or notify the user that page is stale, or something like that.
What I've tried:
The BroadcastChannel API fits my needs perfectly, but does not work in IE 11 or Safari.
So I tried this polyfill so I could use the BroadcastChannel API everywhere. It worked in IE, but not in Safari (I believe BroadcastChannel remained undefined).
I then tried sysend.js , which uses BroadCastChannel if it's available, otherwise fakes it using localStorage. Their demo page works fine in Safari, but on my site, I found it worked in Safari 9, but not 10-12 (tested using BrowserStack and one real Mac running Safari 12). Debugging their script, it seems the storage event that's supposed to fire when localStorage in a different tab is changed simply doesn't fire. But this is actually only a problem when you have document.domain set, which I do.
I believe this is the same as this old Chrome bug. But whereas Chrome had that issue from 2012-2017, Safari apparently introduced it around 2017?
I haven't found anyone else discussing this bug in Safari, but I can prove this pretty easily. Open two tabs that use the same document.domain value and run these scripts:
Tab A:
$(window).on("storage", function (e) {
alert('storage was modified');
});
Tab B:
localStorage.setItem("test", "123");
In Safari 9, Tab A will pop the alert. In Safari 10+, it won't.
If I remove the document.domain, it works. Note that we are using document.domain on these pages, but they are actually on the same domain in this case. However document.domain is needed for other scenarios across the site, so I can't remove it.
I also tried looking at store.js. It has an event system, but it only seemed to work within the same tab (in any browser). Unless I'm missing something.
So, is there any BroadcastChannel polyfill or similar library that actually works in Safari 10+ when you have a document.domain set? Or any other way to do this?
Notes:
I'm aware that BroadcastChannel and the "storage" event for localStorage only fire for tabs other than the current one. That is not my issue, and is actually desirable to me.
I've also seen posts that make me believe an alternate solution relying on localStorage likely will not work in Private Browsing mode in Safari. EDIT: It appears this was fixed in Safari 11 so it does work, but in my tests it didn't share localStorage with any other tabs, even other Private tabs in the same window. So that's not much help. Ideally, a solution would account for this as well, but at this point I'd be happy with anything that worked in Safari 10+ for me. I did see an example in the store.js project where they said they made it so it would fallback to cookies in that case, so it sounds possible at least.
I tried to think about other ways to do this with a setInterval that checks the localStorage for updates every few seconds or something. Even in theory that seems really hacky and unreliable (how would you know when all pages have "received" the update so you can clear it out?). And how would you know when to do it this hacky way instead of one of the preferred methods using localStorage? Safari 10+ is going to report that it supports localStorage so you can't really feature detect it, right? It "supports" it, it just doesn't work correctly.
I found a workaround, but I'll leave this question open because I'd still love to see a better answer.
As a last resort, I ended up toggling between two different ways of performing the messaging, depending on browser.
Basically, if it's Safari I use https://github.com/pubkey/broadcast-channel (you can get the minified vanilla JS version from https://github.com/pubkey/broadcast-channel/blob/master/dist/lib/browser.min.js). This seems to work in all versions even when you have a document.domain set. I think it uses indexDB in this case, which seems like total overkill, but I don't seem to have a choice.
It also works in Safari private windows in newer versions of Safari. I have try catches in place in all my script for older versions of Safari in private mode where it would otherwise throw errors.
If it's not Safari, I use sysend.js which uses BroadcastChannel by default and falls back to localStorage for cases like IE 11.
I'm building an application in React in which I need to record the users' webcam video and audio. Thus far it works in Chrome and Firefox (as expected), but I'm currently trying to find a fallback or a polyfill for Edge and Safari, since I want to be able to support those browsers as well.
Have any been made available? I have not been able to find any information whatsoever in regards to this.
For a live demonstration see: http://codepen.io/rrorg/pen/WxPjrz?editors=0010
When playing a HTTP audio live stream in Safari, the analyser's getByteFrequencyData fills the Array with zeroes.
In all other browsers this works as expected, and Safari has no problems correctly populating the frequency data for static files.
CORS headers are correctly set, the Apple documentation mentions no special cases.
You're not going to like this: Safari does not support createMediaElementSource.
Source: http://caniuse.com/#feat=audio-api ,
It's due to not support for: http://caniuse.com/#feat=stream
Solution? ...adobe flash :(
Latest Safari nightly webkit seems to have solved this but that doesn't solve the problem for now :/
So for those who worked on iOS web applications probably know that due to Apple's policy, Chrome and other mobile browsers on iOS use a very ancient javascript engine. This is why we need to disable some of the rendering for Chrome and other none-safari browsers for iOS.
I had been reading this question and there is no useful answer there. Here are some of my approaches (failed):
Use is_iOS = navigator.userAgent.match(/(iPad|iPhone|iPod)/g) to detect if the browser is on iOS. And then use:
is_FF = navigator.userAgent.match(/(firefox)/g) and is_Chrome = navigator.userAgent.match(/(chrome)/g)
to kill off those firefox and chrome browsers.
I then realized all the browsers share identical user agent string with iOS safari. So I imagine the method should be running a javascript function that only Safari can run and then determine if the browser is Safari.
So, there's two schools of thought when choosing which native functionality you should use while working in browsers. One school of thought is checking the userAgent as you are doing here and using/removing features based on the userAgent.
The problem with checking userAgent is that it gets complicated really fast. You have already run into an interesting problem, but what will happen when you find that ie8 does not have the feature you are looking for?
A better solution may be to check if the feature exists in its current context without worrying about userAgent. A great example would be the new HTML5 audio element. Based on browser support, we can tell that it does not exist in ie8 nor Safari < 4.0. Instead of checking if the browser matches the ones mentioned here you can just check if the feature exists. As we know that the audio element is an instance of the Audio class. We can simply check:
if (window.Audio) {
//load and play the HTML5 audio element here
}
That is much simpler than checking the userAgent.
I have an IndexedDB-based app. Previously it was easy to do feature detection on IndexedDB - just see if window.indexedDB is defined or not. However, two things have changed recently:
Safari was released with very broken IndexedDB support
I rewrote part of my app to use features (compound indexes and multientry indexes) that IE does not support
So now I need some more fine-grained feature detection to show a reasonable error message to Safari and IE users.
I can imagine testing for IE's missing features wouldn't be too difficult, since at least the problems are documented. But for Safari, there are multiple bugs and I don't know how well it's documented anywhere. Heck, I don't even own any Apple devices that I can run Safari on. And what if some other browser in the future releases a buggy IndexedDB implementation?
Has anyone thought about or solved these problems before? Seems to me like there should be some standard IndexedDB feature detection library, but I can't find anything (Modernizr's IndexedDB stuff doesn't seem sufficient).
For IE you can check: IDBKeyRange.only([1]), this throws an Exception DataError on IE.
For Safari I did some useragent sniffing because I couldn't find a way to check, (I could have created a dummyDb and check with the inserts if it would remove my other values) but that would be asynchronous making everything a bit more difficult)..