I've added a video to my site using the default HTML5 video player. The code is like this:
<video width="100%" height="100%" controls>
<source src="http://media.sublimevideo.net/v/midnight-sun-short-edit-360p.mp4" type="video/mp4">
</video>
I wanted to make it so a click on the video stops or starts the video. So I added this:
onclick="this.paused?this.play():this.pause();"
And all good. Until Firefox 35 adds this very function to the player. So now you can only play the video by right clicking and selecting play - an ordinary click would first make the video play via the native behaviour, and then immediately pause it via my click handler. Terrible. So I thought up a JavaScript function something like this:
function startstop() {
if ( FirefoxVersionNumber > 34 ) {
// do nothing
} else {
// start or stop video
}
}
The bit I'm stuck on is how to check the browser version? All the ones I tried returned that Firefox version number was 5... which I think comes from the Netscape part.
You need to prevent the default behaviour of the click event, in much the same way that you would prevent the default behaviour of a form submit if you were handling it yourself with JavaScript.
Event.preventDefault is the tool for the job.
Just do
video.addEventListener('click', function (event) {
event.preventDefault(); // Prevent the default behaviour in Firefox
// Then toggle the video ourselves
if (this.paused) {
this.play();
}
else {
this.pause();
}
});
Here's a Fiddle that works both in Chrome (which has no built-in click-to-toggle behaviour on videos) and in Firefox (which, at least in recent versions, does): http://jsfiddle.net/LjLgkk71/
As an aside, as a general rule you should forget browser sniffing until you've truly and utterly exhausted all other avenues (with the exception of using it to work around specific known quirks and bugs in old browsers, relating to behaviour that has since been fixed or standardised). The idea you expressed in the question, of simply not applying your click handler on certain browser versions, was misguided; you have no way of knowing (and nor do I) what other browsers share or will one day share Firefox's behaviour. If you'd taken your approach, it's almost inevitable that it would come back to bite you either when one of the major browsers followed Firefox's example or when one of your users tried to use your site on a Nintendo DS or something.
There may very likely be better ways to handle this, but here's one that I've come up with: I looked through the release notes for Firefox 35, and it looks like one of the changes made in 35 was fixing a bug where a method called .hasAttributes() that, according to spec, is supposed to be located on Element was previously located on Node. So, although it looks odd, you could do something like:
if(typeof InstallTrigger !== 'undefined' &&
typeof Element.prototype.hasAttributes !== 'undefined') {
// is Firefox >= 35
}
This is based on the fact that typeof InstallTrigger !== 'undefined' would identify Firefox as per this answer and we know .hasAttributes moved to Element beginning in version 35. This would be preferable to user agent parsing because unlike the User Agent string it is unlikely to be spoofed in any way.
It's been mentioned in the comments that it seems odd to do a form of browser detection by checking for the presence of an unrelated JavaScript object - but this is a practice that's established and has been used historically to detect versions of a specific browser greater than a certain version: Here's an article that describes commonly used variables that can be used to detect Internet Explorer versions >= a given number.
Related
I need to hide only one video control from safari browser which is Picture in Picture (PiP). Is there is any solution for that ?
Can we hide this using HTML5?
disablePictureInPicture attribute can be used for that now
<video disablepictureinpicture controlslist="nodownload"></video>
details here:
https://wicg.github.io/picture-in-picture/#disable-pip
There doesn't seem to be any official way to do it...
Currently in Safari it's all or nothing (with controls=false).
The shadow-targetting pseudo elements selectors doesn't seem to work on this browser, unlike on Blink browsers, and even though I found references of an ::-webkit-media-controls-picture-in-picture-button on the Internet.
So for today, the only solution is to disable all the default controls of your video element and to build you own controls.
If you wish, you can make it conditional though:
if(typeof vidElem.webkitSupportsPresentationMode === 'function' &&
vidElem.webkitSupportsPresentationMode('picture-in-picture') ) {
vidElem.controls = false;
buildCustomControls(vidElem);
}
For the future, there might a controlsList attribute, which drafts specs currently only handles nodownload, nofullscreen and noremoteplayback, but I can't see why a nopicture-in-picture couldn't make its way there, if this attribute (mainly motivated by Blink browsers, once again) ever gets out of the drafts.
Is there a way to do feature detection for setDragImage of HTML5 Drag and Drop (in JavaScript or Dart)?
I do the general HTML5 Drag and Drop feature detection with the following (from guide to detecting everything):
return 'draggable' in document.createElement('span');
This will return true for Chrome, Firefox, etc., and IE10. It will return false for IE9.
Now, the problem is with IE10: While it supports most of HTML5 Drag and Drop, setDragImage is not supported and I need to provide a polyfill just for setDragImage. But I couldn't figure out a way how to detect this.
This solution assumes general D&D support has already been checked.
JavaScript (tested in IE, Firefox, Opera and Chrome):
function test() {
var testVar = window.DataTransfer || window.Clipboard; // Clipboard is for Chrome
if("setDragImage" in testVar.prototype) {
window.alert("supported");
} else {
window.alert("not supported");
}
}
Dart:
I didn't find a way to do this with "native" Dart code, so js-interop is the way to go.
You can use the setDragImage-IE polyfill:
https://github.com/MihaiValentin/setDragImage-IE
This is how it actually works (from the README):
I noticed that if you make a change to the element's style (adding a
class that changes appearance) inside the dragstart event and then
removing it immediately in a setTimeout, Internet Explorer will make
a bitmap copy of the modified element and will use it for dragging.
So, what this library actually does is implement the setDragImage
method that changes the target's element style by adding a class that
includes the image that you want to appear while dragging, and then
removes it. In this way, the browser displays the temporary style of
the element as the drag image.
The feature detection mentioned in the previous answer fails in Opera 12 -- because it claims support for setDragImage, it just doesn't work. The Dart libraries that have been linked to also fail entirely in Opera 12, throwing multiple errors to the console.
It's actually not possible to polyfill a ghost image -- even if you create a document element and position it in the right place, you can't get rid of the default one without setDragImage.
The only solution I know of is to filter-out Opera 12 and all versions of IE (up to and including IE11) and treat them as legacy browsers, which have to be catered for with traditional mouse-event scripting. Since the direct feature testing fails, I would recommend an indirect object test (i.e. use an object test to detect those specific browsers):
var hasNativeDraggable = (element.draggable && !(document.uniqueID || window.opera));
I am using jQuery 1.5 and jPlayer 2.0.0, please ignore the fact that this is going to auto play music, I am going to supply enough warning before it starts, the user has to press a big button to initiate the page and I will provide a big STOP button.
I would like the audio file to begin playing and a function be called after the page is ready and the audio is loaded.
I have the following javascript (I have modified the code so it can be understood better).
function FemeMusic() {
FemeMusic.prototype.addMusic = function(actionAfterMusic) {
$("#jp").jPlayer({
ready: function() {
var jPlayerElement = $(this);
jPlayerElement.jPlayer('setMedia', {
mp3: 'aSongByJamesBlunt.mp3'
});
jPlayerElement.bind($.jPlayer.event.progress,
function(event) {
if (event.jPlayer.status.seekPercent === 100) {
jPlayerElement.jPlayer("play");
}
if (actionAfterMusic) {
actionAfterMusic.call();
}
});
},
swfPath: "/flash",
preload: "auto"
});
}
}
I have this function to trigger the above code.
$(document).ready(function() {
var femeMusic = new FemeMusic();
femeMusic.addMusic(killMyself);
});
I was using the same code but with the $.jPlayer.event.loadeddata event without the if statement instead of $.jPlayer.event.progress and it worked fine in some browsers but isn't compatible when it comes to using flash because it doesn't fire the event (Also I had a problem with it in firefox 3.5 but I don't know why). The above seems to work in Firefox
I read this
"I suggest you use the progress event instead and check that the:
event.jPlayer.status.seekPercent === 100. "
On this post http://groups.google.com/group/jplayer/browse_thread/thread/9dc9736f9d6947bd?pli=1 by the creator
Any one got any suggestions?
Edit
Having not got this working in chrome and safari I tested this on a new version 3.6 of Firefox (which I should have done in the first place) and it worked as expected, despite the fact that in the documentation event.progress id described as:
A robust jPlayer event supported by Flash and HTML5 browsers
and in the link above the creator suggests to use event.progress to get round browser problems I can only assume it's a browser specific issue.
Would be nice to get some clarification on this though.
From the creator of jPlayer:
So your goal here is to auto-play AFTER the media is buffered?
If you want the short answer. Forget it. x-browser buffering is a mine
field. Some simply ignore it as you have found. Just setMedia and play it.
If you really want to try and do it. Then a play then pause would help,
since a media.play() is better x-browser than a media.load() i.e. its
operation varies less. The iOS Safari would ignore the play completely
though. The user would need to click on something that starts the
operation i.e. a play button.
That last point applies to trying any form of autoplay, so ensure that there
is a play button available. The jPlayer interface will cope with iOS not
playing through feedback from the browser events i.e. stalled.
I had problems with the progress event with flash and html5 backend in most browsers. as far as I can see the timeupdate event is working much better.
Further to this question, I'm getting a curious result when binding a function to the change event of the Storage object in Chrome 8.0.552.224.
The test:
<!DOCTYPE html>
<html>
<head>
<title>Chrome localStorage Test</title>
<script type="text/javascript" >
var handle_storage = function () {
alert('storage event');
};
window.addEventListener("storage", handle_storage, false);
</script>
</head>
<body>
<button id="add" onclick="localStorage.setItem('a','test')">Add</button>
<button id="clear" onclick="localStorage.clear()">Clear</button>
</body>
</html>
Open up the page in two Chrome windows, one window with two tabs,
Click the 'Add' button
When I do this I get an alert box displayed on on the second tab and on the second window but not on the tab that invoked the event (the I clicked on). As I understand it I should see three alert boxes (one for each tab opened).
Is this a bug? Is anyone else getting this behaviour? If not what version are you running? Or have I just got this all completely wrong?
Update and final conlusion
It turns out that the spec actually says that this is the desired behavior, therefore IE9's implementation is broken.
4.2 The sessionStorage attribute
When the setItem(), removeItem(), and clear() methods are called on a Storage object x ... if the methods did something, then in every HTMLDocument ... [that is] associated with the same storage area, other than x, a storage event must be fired....
So as we can see, the spec does do a really bad job at making it clear, that this is the specified behavior. That was the reason why Opera 10's implementation was broken and it's most likely also the reason why IE9's implementation is broken.
What do we learn from that? Always read every, single, word of the specification (especially if you're implementing the stuff...).
Old answer
As you said the basic behavior here is "Invoke on all but the current page".
There's an old Chrome Bug Report from last July.
As one can read there, Firefox has the same "issue". I've tested this with the latest nightly, still behaves the same way like in Chrome.
Another test in Opera 11 shows that this must be some kind spec'ed behavior, since Opera 11 does the exact same thing but Opera 10 did fire events on all windows / tabs. Sadly the official changelogs for Opera 11 do not state any change for this behavior.
Reading through the specification, nothing there states this behavior. The only thing I could find is:
The storage event is fired when a storage area changes, as described in the previous two sections (for session storage, for local storage).
When this happens, the user agent must queue a task to fire an event with the name storage, which does not bubble and is not cancelable, and which uses the StorageEvent interface, at each Window object whose Document object has a Storage object that is affected.
Note: This includes Document objects that are not fully active, but events fired on those are ignored by the event loop until the Document becomes fully active again.
Well, what does the Note mean?
From another specification:
A Document is said to be fully active when it is the active document of its browsing context, and either its browsing context is a top-level browsing context, or the Document through which that browsing context is nested is itself fully active.
Doesn't make sense? Yes. Does it help us in any way? No.
So we (in the JavaScript Chatroom) poked on #whatwg to see what all of this is about, so far there has been no response. I'll update my answer as soon as we get any response.
To conclude for now
Firefox, Chrome, Safari and Opera have the exact same behavior. That is, they do not fire on the tab / window that made the chance to localStorage.
But IE9 Beta behaves like Opera 10, so it fires on all tabs / windows.
Since the author of the localStorage spec works at Google in R&D, I hardly doubt Chrome would get this wrong. And since there are no Bugs on this over at Bugzilla and Opera changed the behavior in 11, it seems that this is the way it should work. Still no answer why it works this way and why IE9 behaves differently, but we're still waiting for a response from #whatwg.
Ivo Wetzel's answer for what is going wrong is correct.
I was having the same problem but managed to work around this limitation in the spec by manually creating and firing a StorageEvent in the tab that initiated the storage change (see StorageEvent#initStorageEvent).
I'm currently working on a jQuery plugin that tracks a visitors mouse behavior. Movements, clicks, scrolling and resizing are all recorded and sent, via Ajax, to a location where this data is parsed and stored.
Originally, the data is sent to a script when the user leaves the page. By 'leaves' I'm referring to refreshing, going back and forth though history, closing the window/tab and going to a different address.
The solution works in all browsers EXCEPT for Opera. I'm using jQuery's 'unload' event which isn't supported by Opera at all. Neither is onbeforeunload or onunload.
The question is, how do I implement this kind of functionality for Opera browsers?
One solution I had was to make special use of a 'polling' feature I created. This feature allows you to specify an interval which pushes the content to the server every 'x' seconds. Setting this to 1 second specifically for Opera browsers would probably solve this issue, but it's an awful amount of overhead and the requests aren't always completed in sequence, etc ...
Any suggestions or am I only stuck with the above option?
Thanks!
I suppose I could just link you guys to the plugin source. http://www.thedrunkenepic.com/junk/jquery.mousalytics.js
Regarding the code linked above, adding:
if(window.opera)
{
options.interval = 1;
}
On line 89 works great. My only concern is overhead, so I'm still looking for a more elegant solution.
According to http://bytes.com/topic/javascript/insights/799229-browser-quirk-onload-onunload-do-not-fire-back-forward-refresh-opera, Opera never really fires onload / onunload events, so functionality like this isn't possible without hacks.
http://dev.opera.com/articles/view/efficient-javascript/?page=4 seems to confirm this, and basically states that opera tries to maintain the state of the page across requests.
On further investgation, http://unitehowto.com/Onunload indicates that it might be possible with opera.io.webserver.addEventListener('_close', onunload, false); (where onunload is a previously defined function), however it also indicates that this functionality is not consistent across all versions of opera, and might not work at all.
I think that your best option is probably to use the polling option for Opera, or possibly use a server-side check for the current page and where it falls in the history queue.
Does adding this line of JavaScript work for you?
history.navigationMode = 'compatible';
Source: http://www.opera.com/support/kb/view/827/
I've had the same problem and this saved my day:
if( typeof(opera) != 'undefined' )
{
opera.setOverrideHistoryNavigationMode( 'compatible' );
history.navigationMode = 'compatible';
}
More info about this problem can be found at: http://www.opera.com/support/kb/view/827/