I use a Javascript solution which loads the youtube player API asynchronously.
The whole script is supposed to play the video when scrolled to its position.
It works in all browsers and also in IE(11), but sometimes in IE I get an error in Developer Tools: SCRIPT445 (Object doesn't support this action).
The Youtube Player still works but it seems to crash other scripts. I looked around in the web and also here on Stackoverflow. There seem to be others who have similar problems but they were too specific. Maybe someone could help me with this one. Here is the part of the code which makes the problem:
var yt_int, yt_players={},
initYT = function() {
$(".ytplayer").each(function() {
yt_players[this.id] = new YT.Player(this.id); <-- Error line
});
};
$.getScript("//www.youtube.com/player_api", function() {
yt_int = setInterval(function(){
if(typeof YT === "object"){
initYT();
clearInterval(yt_int);
}
},500);
});
object doesn't support this action is error is coming in IE11 using window.dispatchEvent(new Event('resize')); we need to d handle the condition for ie11.
if (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0) {
var evt = document.createEvent('UIEvents');
evt.initUIEvent('resize', true, false, window, 0);
window.dispatchEvent(evt);
} else {
window.dispatchEvent(new Event('resize'));
}
There is a race condition in IE that is firing off your script loader callback before the entire script is evaluated. By using setTimeout(initYT, 0) you will allow the script to finish evaluating before firing your initialization function.
Related
I have an embedded Youtube video, and I want to capture events when the user pauses the video, skips around, etc. Following the examples at Google's documentation (http://developers.google.com/youtube/js_api_reference), I do the following:
var video = (my video ID here);
var params = { allowScriptAccess: "always" };
var atts = { id: "youtubeplayer" };
var x = 854;
var y = 480;
swfobject.embedSWF("http://www.youtube.com/v/" + video + "?enablejsapi=1&playerapiid=ytplayer&version=3", "bobina", x, y, "8", null, null, params, atts);
function onYouTubePlayerReady(playerId) {
youtubeplayer = document.getElementById('youtubeplayer');
youtubeplayer.addEventListener("onStateChange", "onytplayerStateChange");
youtubeplayer.setPlaybackQuality('large');
}
function onytplayerStateChange(newState) {
//Make it autoplay on page load
if (newState == -1) {
youtubeplayer.playVideo();
}
var tiempo=youtubeplayer.getCurrentTime();
//Rounds to 2 decimals
var tiempo = Math.round(tiempo*100)/100;
alert(tiempo);
}
Now, all this works perfectly in Firefox and Safari, but there's no way to make it work in IE8 (by which I mean: the video loads, but events aren't captured: the event handler never gets called). According to javascript addEventListener onStateChange not working in IE , IE doesn't support addEventListener(), so I tried replacing that line with:
youtubeplayer.attachEvent("onStateChange", onytplayerStateChange);
But it doesn't work either. (The author of that question says that he finally solved it by putting "onComplete in my colorbox and put the swfobject in that", but I'm not using a colorbox here (I don't even know what that is), so I don't understand what he means.
Using alerts() to debug, I see that the first function (onYoutubePlayerReady()) is indeed called, but I can't even tell whether the event listener isn't being registered or whether it's the getElementById() that isn't working; I tried debugging by adding a:
alert(typeof youtubeplayer);
Right after it, but it doesn't even pop up.
Oh, and one more thing: the video starts automatically in Firefox and Safari, but in IE it doesn't either. (And yes, I'm running this on a server, not on a local page).
Anyone has any ideas? I don't really know what else to try.
Okay, found the solution. Guess what it was? Just write is as:
var youtubeplayer = document.getElementById('youtubeplayer');
And it works perfectly in IE 8. All it needed was the "var" keyword.
Just in case it's useful for someone else with the same problem...
IE9 is showing false complete property with the following:
$("<img/>",{src:"http://farm2.static.flickr.com/1104/1434841504_edc671e65c.jpg"}).each(function(){console.log(this.complete);});
If you run this code in a browser console, (allow enough time for the image to load) then run it again. IE9 is the only browser I've tested showing false the second time. This seems to be a known bug, from some simple google searching.
I need a workaround if anyone has one.
This could be a timing issue, as letting the code above set a global variable a la:
var img = $("<img....
and then testing that variable's properties gives different results:
img[0].complete === true
and
img[0].readyState === "complete"
There must be some other way of getting this infomation. Any ideas... Thanks!
I use this:
function doWhenLoaded(obj,callback) {
if($.browser.msie) {
var src=$(obj).attr("src");
$(obj).attr("src","");
$(obj).attr("src",src);
}
$(obj).one("load",callback).each(function(){
// alert(this.readyState+" "+this.src);
if(
this.complete
|| this.readyState == "complete"
|| this.readyState == 4
|| ($.browser.msie && parseInt($.browser.version) == 6)
) {
$(this).trigger("load");
}
});
}
A sample:
doWhenLoaded("#main_background_img",function(){
$("#main_background_img").slideDown(1000);
});
This is how i usually preload an image:
var img = new Image();
$(img).attr('src', "foo.jpg");
if (img.complete || img.readyState === 4) {
// image is cached
alert("the image was cached!");
} else {
$(img).load(function() {
// image was not cached, but done loading
alert("the image was not cached, but it is done loading.");
});
}
I haven't deeply debugged it in IE9, but I haven't ran into any issues with it.
the code was pulled from https://github.com/tentonaxe/jQuery-preloadImages/blob/master/jquery.preloadimages.js and modified.
You could try an AJAX request on the image and see the status code. If it's 304 it means the image was cached. Not sure how well that would work though. Maybe AJAX does some cache-busting.
I know this was asked a million years ago, but I figure I would contribute my solution which is similar but has less overhead and i/o.
Basically, you create a custom jQuery method that performs the similar feats all in one function:
$.fn.imgLoad = function(callback) {
return this.each(function() {
if(callback){
if(this.complete || (this.readyState === 4) || (this.readyState === 'complete')) {
callback.apply(this);
} else {
$(this).one('load.imgCallback', function(){
callback.apply(this);
});
}
}
});
}
This consolidates the checking of all possible events into one (both cached and uncached), but also makes the method chainable. You can call it with:
$('img').imgLoad(function(){
console.log('loaded');
});
Works cross-browser, back to IE6. Notice it checks for caching first, and if not triggers a namespaced load event only once to prevent reloads in case you call the function with that logic twice, but also to allow custom load events to be bound (if applicable).
You can't tell if it's cached, but you can force a fresh load by "salting" the filename:
src:"http://farm2.static.flickr.com/1104/1434841504_edc671e65c.jpg?"+new Date()
I am using History API for my web app and have one issue.
I do Ajax calls to update some results on the page and use history.pushState() in order to update the browser's location bar without page reload. Then, of course, I use window.popstate in order to restore previous state when back-button is clicked.
The problem is well-known — Chrome and Firefox treat that popstate event differently. While Firefox doesn't fire it up on the first load, Chrome does. I would like to have Firefox-style and not fire the event up on load since it just updates the results with exactly the same ones on load. Is there a workaround except using History.js? The reason I don't feel like using it is — it needs way too many JS libraries by itself and, since I need it to be implemented in a CMS with already too much JS, I would like to minimize JS I am putting in it.
So, would like to know whether there is a way to make Chrome not fire up popstate on load or, maybe, somebody tried to use History.js as all libraries mashed up together into one file.
In Google Chrome in version 19 the solution from #spliter stopped working. As #johnnymire pointed out, history.state in Chrome 19 exists, but it's null.
My workaround is to add window.history.state !== null into checking if state exists in window.history:
var popped = ('state' in window.history && window.history.state !== null), initialURL = location.href;
I tested it in all major browsers and in Chrome versions 19 and 18. It looks like it works.
In case you do not want to take special measures for each handler you add to onpopstate, my solution might be interesting for you. A big plus of this solution is also that onpopstate events can be handled before the page loading has been finished.
Just run this code once before you add any onpopstate handlers and everything should work as expected (aka like in Mozilla ^^).
(function() {
// There's nothing to do for older browsers ;)
if (!window.addEventListener)
return;
var blockPopstateEvent = document.readyState!="complete";
window.addEventListener("load", function() {
// The timeout ensures that popstate-events will be unblocked right
// after the load event occured, but not in the same event-loop cycle.
setTimeout(function(){ blockPopstateEvent = false; }, 0);
}, false);
window.addEventListener("popstate", function(evt) {
if (blockPopstateEvent && document.readyState=="complete") {
evt.preventDefault();
evt.stopImmediatePropagation();
}
}, false);
})();
How it works:
Chrome, Safari and probably other webkit browsers fire the onpopstate event when the document has been loaded. This is not intended, so we block popstate events until the the first event loop cicle after document has been loaded. This is done by the preventDefault and stopImmediatePropagation calls (unlike stopPropagation stopImmediatePropagation stops all event handler calls instantly).
However, since the document's readyState is already on "complete" when Chrome fires onpopstate erroneously, we allow opopstate events, which have been fired before document loading has been finished to allow onpopstate calls before the document has been loaded.
Update 2014-04-23: Fixed a bug where popstate events have been blocked if the script is executed after the page has been loaded.
Using setTimeout only isn't a correct solution because you have no idea how long it will take for the content to be loaded so it's possible the popstate event is emitted after the timeout.
Here is my solution:
https://gist.github.com/3551566
/*
* Necessary hack because WebKit fires a popstate event on document load
* https://code.google.com/p/chromium/issues/detail?id=63040
* https://bugs.webkit.org/process_bug.cgi
*/
window.addEventListener('load', function() {
setTimeout(function() {
window.addEventListener('popstate', function() {
...
});
}, 0);
});
The solution has been found in jquery.pjax.js lines 195-225:
// Used to detect initial (useless) popstate.
// If history.state exists, assume browser isn't going to fire initial popstate.
var popped = ('state' in window.history), initialURL = location.href
// popstate handler takes care of the back and forward buttons
//
// You probably shouldn't use pjax on pages with other pushState
// stuff yet.
$(window).bind('popstate', function(event){
// Ignore inital popstate that some browsers fire on page load
var initialPop = !popped && location.href == initialURL
popped = true
if ( initialPop ) return
var state = event.state
if ( state && state.pjax ) {
var container = state.pjax
if ( $(container+'').length )
$.pjax({
url: state.url || location.href,
fragment: state.fragment,
container: container,
push: false,
timeout: state.timeout
})
else
window.location = location.href
}
})
A more direct solution than reimplementing pjax is set a variable on pushState, and check for the variable on popState, so the initial popState doesn't inconsistently fire on load (not a jquery-specific solution, just using it for events):
$(window).bind('popstate', function (ev){
if (!window.history.ready && !ev.originalEvent.state)
return; // workaround for popstate on load
});
// ... later ...
function doNavigation(nextPageId) {
window.history.ready = true;
history.pushState(state, null, 'content.php?id='+ nextPageId);
// ajax in content instead of loading server-side
}
Webkit's initial onpopstate event has no state assigned, so you can use this to check for the unwanted behaviour:
window.onpopstate = function(e){
if(e.state)
//do something
};
A comprehensive solution, allowing for navigation back to the original page, would build on this idea:
<body onload="init()">
page 1
page 2
<div id="content"></div>
</body>
<script>
function init(){
openURL(window.location.href);
}
function doClick(e){
if(window.history.pushState)
openURL(e.getAttribute('href'), true);
else
window.open(e.getAttribute('href'), '_self');
}
function openURL(href, push){
document.getElementById('content').innerHTML = href + ': ' + (push ? 'user' : 'browser');
if(window.history.pushState){
if(push)
window.history.pushState({href: href}, 'your page title', href);
else
window.history.replaceState({href: href}, 'your page title', href);
}
}
window.onpopstate = function(e){
if(e.state)
openURL(e.state.href);
};
</script>
While this could still fire twice (with some nifty navigation), it can be handled simply with a check against the previous href.
This is my workaround.
window.setTimeout(function() {
window.addEventListener('popstate', function() {
// ...
});
}, 1000);
Here's my solution:
var _firstload = true;
$(function(){
window.onpopstate = function(event){
var state = event.state;
if(_firstload && !state){
_firstload = false;
}
else if(state){
_firstload = false;
// you should pass state.some_data to another function here
alert('state was changed! back/forward button was pressed!');
}
else{
_firstload = false;
// you should inform some function that the original state returned
alert('you returned back to the original state (the home state)');
}
}
})
The best way to get Chrome to not fire popstate on a page load is to up-vote https://code.google.com/p/chromium/issues/detail?id=63040. They've known Chrome isn't in compliance with the HTML5 spec for two full years now and still haven't fixed it!
In case of use event.state !== null returning back in history to first loaded page won't work in non mobile browsers.
I use sessionStorage to mark when ajax navigation really starts.
history.pushState(url, null, url);
sessionStorage.ajNavStarted = true;
window.addEventListener('popstate', function(e) {
if (sessionStorage.ajNavStarted) {
location.href = (e.state === null) ? location.href : e.state;
}
}, false);
The presented solutions have a problem on page reload. The following seems to work better, but I have only tested Firefox and Chrome. It uses the actuality, that there seems to be a difference between e.event.state and window.history.state.
window.addEvent('popstate', function(e) {
if(e.event.state) {
window.location.reload(); // Event code
}
});
I know you asked against it, but you should really just use History.js as it clears up a million browser incompatibilities. I went the manual fix route only to later find there were more and more problems that you'll only find out way down the road. It really isn't that hard nowadays:
<script src="//cdnjs.cloudflare.com/ajax/libs/history.js/1.8/native.history.min.js" type="text/javascript"></script>
And read the api at https://github.com/browserstate/history.js
This solved the problem for me. All I did was set a timeout function which delays the execution of the function long enough to miss the popstate event that is fired on pageload
if (history && history.pushState) {
setTimeout(function(){
$(window).bind("popstate", function() {
$.getScript(location.href);
});
},3000);
}
You can create an event and fire it after your onload handler.
var evt = document.createEvent("PopStateEvent");
evt.initPopStateEvent("popstate", false, false, { .. state object ..});
window.dispatchEvent(evt);
Note, this is slightly broke in Chrome/Safari, but I have submitted the patch in to WebKit and it should be available soon, but it is the "most correct" way.
This worked for me in Firefox and Chrome
window.onpopstate = function(event) { //back button click
console.log("onpopstate");
if (event.state) {
window.location.reload();
}
};
I have two colorbox popup boxes which show a YouTube video in each. When they're finished playing, I'm trying to have them automatically close the colorbox window. This code below works perfect in Firefox, but in IE I can't get addEventListener to work. I've tried attachEvent with no success. Can anybody offer any suggestions as to how to solve this? It seems simple but I'm exhausted trying to find a solution.
UPDATE 1:
Well, this is my current code. It works perfect in Firefox, but IE only outputs good. IE8 debugger doesn't report any errors either...
function onYouTubePlayerReady(playerId) {
if (playerId && playerId != 'undefined') {
if(playerId && playerId == 'ytvideo1'){
var ytswf = document.getElementById('ytplayer1');
alert('good');
} else if(playerId && playerId == 'ytvideo2'){
var ytswf = document.getElementById('ytplayer2');
} else {
}
setInterval('', 1000);
ytswf.addEventListener('onStateChange', 'onytplayerStateChange');
alert('great');
}
}
function onytplayerStateChange(newState) {
alert('amazing');
if(newState == 0){
$.fn.colorbox.close();
alert('perfect');
}
}
Update 3: Solution
Simply put onComplete in my colorbox and put the swfobject in that and it worked perfectly in IE.
IE doesn't support addEventListener does it?? You need attachEvent right?
if (el.addEventListener){
el.addEventListener('click', modifyText, false);
else if (el.attachEvent){
el.attachEvent('onclick', modifyText);
}
from testing in IE it looks like the reference you are using
ytswf = document.getElementById('ytplayer1');
is assigned before the actual swf object is loaded, so IE thinks you are referring to a simple div element
you need to run this code
function onYouTubePlayerReady(playerId) {
ytswf = document.getElementById("ytplayer1");
ytplayer.addEventListener("onStateChange", "onytplayerStateChange");
}
right after you call
swfobject.embedSWF("http://www.youtube.com/v/SPWU-EiulRY?
hl=en_US&hd=0&rel=0&fs=1&autoplay=1&enablejsapi=1&playerapiid=ytvideo1",
"popupVideoContainer1", "853", "505", "8", null, null, params, atts);
before you close out that $(function()
and place var ytswf;
right after the <script>
instead of further down
New answer
The YouTube player object implements its own addEventListener method which is more like how AS3's syntax. As per the information listed here:
player.addEventListener(event:String,
listener:String):Void
YouTube provides an example on the page I linked which I'll provide here:
function onYouTubePlayerReady(playerId) {
ytplayer = document.getElementById("myytplayer");
ytplayer.addEventListener("onStateChange", "onytplayerStateChange");
}
function onytplayerStateChange(newState) {
alert("Player's new state: " + newState);
}
YouTube also provides an example page that seems to prove out that their example code works in IE. I'll link that example page here.
Now, here's an attempt at re-writing the pertinent parts of your code to work as per the examples provided by Google/YouTube:
function onYouTubePlayerReady(playerId) {
if(playerId && playerId == 'ytvideo1'){
var ytplayer = document.getElementById('ytplayer1');
} else if(playerId && playerId == 'ytvideo2'){
var ytplayer = document.getElementById("ytplayer2");
} else {
return;
}
ytplayer.addEventListener('onStateChange', 'onytplayerStateChange');
}
So, it turns out that the mistake being made here arises from the confusion created by the use of the method name 'addEventListener'. In the W3C JavaScript implementation, the second parameter is a function while in the YouTube implementation, the second parameter is a string. Give this a shot =).
I've written a userscript for Gmail : Pimp.my.Gmail & i'd like it to be compatible with Google Chrome too.
Now i have tried a couple of things, to the best of my Javascript knowledge (which is very weak) & have been successful up-to a certain extent, though im not sure if it's the right way.
Here's what i tried, to make it work in Chrome:
The very first thing i found is that contentWindow.document doesn't work in chrome, so i tried contentDocument, which works.
BUT i noticed one thing, checking the console messages in Firefox and Chrome, i saw that the script gets executed multiple times in Firefox whereas in Chrome it just executes once!
So i had to abandon the window.addEventListener('load', init, false); line and replace it with window.setTimeout(init, 5000); and i'm not sure if this is a good idea.
The other thing i tried is keeping the window.addEventListener('load', init, false); line and using window.setTimeout(init, 1000); inside init() in case the canvasframe is not found.
So please do lemme know what would be the best way to make this script cross-browser compatible.
Oh and im all ears for making this script better/efficient code wise (which im sure is possible)
edit: no help...? :'(
edit 28-Apr:
i re-wrote the code a little and now it looks something like this.:
if(document.location != top.location) return;
(function() {
var interval = window.setInterval(waitforiframe, 3000);
var canvas;
function waitforiframe() {
console.log("Finding canvas frame");
canvas = document.getElementById("canvas_frame");
if (canvas && canvas.contentDocument) {
console.log("Found canvas frame");
pimpmygmail();
}
}
function pimpmygmail() {
gmail = canvas.contentDocument;
if(!gmail) return;
window.clearInterval(interval);
console.log("Lets PIMP.MY.GMAIL!!!");
......rest of the code......
})();
This works perfectly fine in Firefox, but in Chrome, it gives me a top is undefined error.
Another thing i noticed is that if i remove the first line if(document.location != top.location) return; , the waitforiframe() method keeps getting called over and over again. (ie i see the "Finding canvas frame" error in the console)
can someone tell me what does the first line do? i mean what does it achieve & why does the waitforiframe() method run forever if i remove that line??
A BIG THANK YOU TO ALL WHO HELPED! -_- meh
btw, this was all i needed at the beginning of the script:
try { if(top.location.href != window.location.href) { return; } }
catch(e) { return; }