I have a web application with an iframe that needs to communicate with its hosting page. The iframe and the host are on different domains and protocols (the iframe is https, the main page http). I use postMessage to get a small bit of state (user tracking) from the outer page into the iframe.
When the iframe is loaded, it sends a short message out to the top page to ask for the visitorid:
if ($.w.top != $.w) $.f.postMessage($.w.top, 'Get visitorId');
($.f.postMessage(w, m) is just a wrapper around postMessage, that does nothing if typeof w.postMessage === 'undefined'). On the outer page, we have a message listener:
// Set up event listener so that we can respond when any iframes
// inside of us ask for our visitorId
$.f.listen($.w, 'message', giveVisitorId);
function giveVisitorId(event) {
$.w['zzzzz'] = event.source;
if (event.data === 'Get visitorId') {
alert('about to reply from '+window.location.href+' with visitorid, typeof event.source.postMessage is ' + typeof(event.source.postMessage));
event.source.postMessage('visitorId=' + $.v.visitorId, '*');
}
}
The inner frame has a listener registered for the response:
$.f.listen($.w, 'message', receiveVisitorId);
function receiveVisitorId(event) {
alert('receiveVisitorId called with: ' + event.data + ' in window '+window.location.href);
var s = event.data.split('=');
if (s[0] === 'visitorId' && s.length === 2) {
$.v.visitorId = s[1];
$.w.clearTimeout(giveUp);
rest();
}
}
This all works as it is supposed to on chrome and firefox on OSX (when the iframe is loaded we get two alerts, one from receiveVisitorId and one from giveVisitorId); however on IE8 on XP we only get the first alert (from giveVisitorId).
This is strange enough, since it seems that the postMessage going out works and the one going in doesn't; what's truly perplexing is that if we go to the console and run zzzzz.postMessage('hi', '*') the alert in receiveVisitorId happens just as expected! (Note that we saved event.source in window.zzzzz).
Does anyone have an idea why this might be happening?
PS: The definitions of $.w.listen and $.w.postMessage, for reference:
listen: function (el, ev, fn) {
if (typeof $.w.addEventListener !== 'undefined') {
el.addEventListener(ev, fn, false);
}
else if (typeof $.w.attachEvent !== 'undefined') {
el.attachEvent('on' + ev, fn);
}
},
postMessage: function(w, m) {
if (typeof w.postMessage !== 'undefined') {
w.postMessage(m, "*");
}
},
We resolved it. The problem was that we were calling $.f.listen after $.f.postMessage in the inner iframe; so when the outer window posted a message to the inner, the listener had not yet been attached. I don't know why this happened in IE and not chrome or firefox, but we just put it down to timing differences between the browsers.
Related
I got an error and strange behavior inside template.onDestoyed;
I have code for infinite scroll subscribtion (it stored in special subscribtion-template) It work fine, until i switch to another route, and create a new instance of subscriber-template.
Code:
Template.subscriber.onCreated(function() {
var template = this;
var skipCount = 0;
template.autorun(function(c) {
template.subscribe(template.data.name, skipCount, template.data.user);
var block = true;
$(window).scroll(function() {
if (($(window).scrollTop() + $(window).height()) >= ($(document).height()) && block) {
block = false;
skipCount = skipCount + template.data.count;
console.log(template.data);
console.log("skip_count is "+skipCount);
template.subscribe(template.data.name, skipCount, template.data.user, {
onReady: function() {
block = true;
},
onStop: function() {
console.log('route switched, subscribtion stopped');
}
});
}
});
})
});
When i "scroll down" on a page, subscriber work fine, when i go in another page and "scroll down" first i get a data from old subscriber template (what must be destroyed in theory) in first time. In second time (scroll down again) new instance of subscriber starts works normally.
PIRNT SCREEN CONSOLE
What i doing wrong?
Owch!
The good guy from meteor forums helped me.
Actually the problem is in jquery.scroll event. It not cleaned up when template is destroyed. (Is it a bug? Or it is normal behavior?). I just needed to unbind the scroll event in onDestroyed section.
I'm working with the Youtube Javascript API (iframe version) and I'm using start and end parameters in order to play a portion only of a video. While everything works fine, once the end is reach, the player stop (normal behavior). However, when clicking again on play, the player does not seems to considering the start option. It always restart playing from the beginning of the video. It ends however at the defined end value, always.
<iframe frameborder="0" src="https://www.youtube.com/embed/5lGoQhFb4NM?autoplay=1&autohide=1&modestbranding=1&showinfo=0&controls=0&vq=hd720&rel=0&start=60&end=100" style="width: 100%; height: 400px;"></iframe>
Do you guys have any idea on why it is happening?
Here's how I was able to have the replay button start over at the original start time, as well as prevent users from scrolling outside the start/end bounds of the playerVars:
function onPlayerStateChange(event) {
var isClip = playerOptions.playerVars.start && playerOptions.playerVars.end;
var start = isClip ? playerOptions.playerVars.start : null;
var end = isClip ? playerOptions.playerVars.end : null;
if (event.data == YT.PlayerState.PLAYING) {
if (isClip) {
var currentTime = event.target.getCurrentTime();
if (currentTime < start || currentTime > end) {
event.target.seekTo(playerOptions.playerVars.start);
}
}
}
}
I've tried the above solution without succes (error console : "playerOptions is not defined")
But thx to this topic YouTube API - Loop video between set start and end times I've been able to successfully get the start/end parameters on replay :
<script>
// 5. The API calls this function when the player's state changes.
var done = false;
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.ENDED) {
var playButton = document.getElementById("play");
playButton.addEventListener("click", function() {
player.loadVideoById({
videoId: videoId,
startSeconds: startSeconds,
endSeconds: endSeconds
});
});
document.getElementById("play").style.cssText = "visibility:visible";
}
}
</script>
Explanations : in my case, I'm using a button id="play" to start the video. When the video is ENDED, the button #play is set to visibility:visible and the click event uses the player.loadVideoById corresponding parameters.
If you don't use the click event, the video will auto loop with the desired start/end values.
From Youtube API reference = https://developers.google.com/youtube/iframe_api_reference?hl=fr#Getting_Started
From a xul-based firefox addon, I need to:
programmatically create an invisible iframe (once)
reuse it to load multiple URLs as the addon runs
access the returned HTML after each URL loads
Problem: I can only get the first page-load for any created iframe to trigger an 'onload' or 'DOMContentLoaded' event. For subsequent URLs, there is no event triggered.
Note: I'm also fine with using the hiddenDOMWindow itself if this is possible...
Code:
var urls = ['http://en.wikipedia.org/wiki/Internet', 'http://en.wikipedia.org/wiki/IPv4', 'http://en.wikipedia.org/wiki/Multicast' ];
visitPage(urls.pop());
function visitPage(url) {
var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
var hiddenWindow = Components.classes["#mozilla.org/appshell/appShellService;1"].getService
(Components.interfaces.nsIAppShellService).hiddenDOMWindow;
var doc = hiddenWindow.document, iframe = doc.getElementById("my-iframe");
if (!iframe)
{
iframe = doc.createElement("iframe");
//OR: iframe = doc.createElementNS(XUL_NS,"iframe");
iframe.setAttribute("id", "my-iframe");
iframe.setAttribute('style', 'display: none');
iframe.addEventListener("DOMContentLoaded", function (e) {
dump('DOMContentLoaded: '+e.originalTarget.location.href);
visitPage(urls.pop());
});
doc.documentElement.appendChild(iframe);
}
iframe.src = url;
}
There are some traps:
The hiddenWindow differs between platforms. It is XUL on Mac, and HTML else.
You should use .setAttribute("src", url); to reliably navigate.
The following works for me (Mac, Win7):
var urls = [
'http://en.wikipedia.org/wiki/Internet',
'http://en.wikipedia.org/wiki/IPv4',
'http://en.wikipedia.org/wiki/Multicast'
];
var hiddenWindow = Components.classes["#mozilla.org/appshell/appShellService;1"].
getService(Components.interfaces.nsIAppShellService).
hiddenDOMWindow;
function visitPage(url) {
var iframe = hiddenWindow.document.getElementById("my-iframe");
if (!iframe) {
// Always use html. The hidden window might be XUL (Mac)
// or just html (other platforms).
iframe = hiddenWindow.document.
createElementNS("http://www.w3.org/1999/xhtml", "iframe");
iframe.setAttribute("id", "my-iframe");
iframe.addEventListener("DOMContentLoaded", function (e) {
console.log("DOMContentLoaded: " +
e.originalTarget.location);
var u = urls.pop();
// Make sure there actually was something left to load.
if (u) {
visitPage(u);
}
});
hiddenWindow.document.documentElement.appendChild(iframe);
}
// Use .setAttribute() to reliably navigate the iframe.
iframe.setAttribute("src", url);
}
visitPage(urls.pop());
Don't reload the hiddenWindow itself, or you will break lots of other code.
I have a parent page which has an iframe and also has javascript which will create a form, append it to the iframe, and submits it via POST to an external URL upon page load.
The content from the external URL then loads in the iframe. This works fine in all browsers EXCEPT IE9.
I tried the 'meta http-equiv="X-UA-Compatible" content="IE=8" ' trick and this didn't help. Sometimes the iframe renders the content, sometimes it doesn't upon refresh. Debug statements in the javascript show it is firing each time (each page load) and Fiddler shows the successful request/response to the external URL. It's as if IE9 selectively decides whether to update the DOM.
Also I've noticed is that if there is any sort of delay with the external request (taking a few seconds), then the iframe content never renders. Has anyone experienced this with IE9 and have a solution?
<iframe frameborder="0" height="600px" id="ifPage" runat="server" width="700px" />
<script type="text/javascript" language="javascript">
var alreadyrunflag = 0 //flag to indicate whether target function has already been run
if (document.addEventListener) {//FireFox or Sarafi
document.addEventListener("DOMContentLoaded", function () { alreadyrunflag = 1; GetExternalPageContent() }, false)
}
else if (document.all && !window.opera)
{//IE
addLoadEvent(GetExternalPageContent)
}
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
}
else {
window.onload = function () {
if (oldonload) {
oldonload();
}
func();
}
}
}
function GetExternalPageContent() {
var iframe = document.getElementsByTagName("iframe");
if (iframe != null) {
var uniqueString = "embFrame";
iframe[0].contentWindow.name = uniqueString;
var form = document.createElement("form");
form.target = uniqueString;
form.action = '<%=ExternalUrl %>';
form.method = "POST";
//parameter submitted to external URL to get appropriate content
var input = document.createElement("input");
input.type = "hidden";
input.name = "embParam";
input.value = "paramValue1";
form.appendChild(input);
document.body.appendChild(form);
form.submit();
}
}
</script>
I just wanted to let people know that the issue here is that IE doesn't like naming of the iframe content window like this:
iframe[0].contentWindow.name = uniqueString
Instead, the name attribute must be within the iframe tag itself. There were no javascript errors indicating this, it just didn't consistently render. Then, when you need to dynamically reference the iframe content, use:
var iframe = window.frames['embFrame']
Doing it this way solved the issue and now the iframe content is rendered consistently.
I am making a asynchronous request to different server for some data using jquery. It works fine in IE, but doesn't work in FireFox and Chrome, when it reaches the code where the request to other server is made, it freezes there and a blank page is shown. If I remove that piece of code, the ajax works fine.
Also, when I place a breakpoint at document.ready, the breakpoint is hit when debugging using IE, but it's not hit when debugging using FireFox.
Following is the JQuery I am using
jQuery(document).ready(function ($) {
$('.tabs a, .tabs span').livequery('click', function () {
var currentTab = $(this).parents('li:first');
if (!currentTab.is('.active')) {
var currentContent = $('.tab_container .' + currentTab.attr('class'));
$('.tabs li').removeClass("active");
currentTab.addClass("active");
var url = $(this).attr("href");
var newContent = "";
if (currentContent.length == 0) {
$.get(url, {}, function (result) {
$('#tabs.tab_container div:visible').fadeOut(100, function () {
$('#tabs.tab_container')
.html(result)
.fadeIn(100);
});
}, 'html');
}
else {
$('#tabs.tab_container div:visible').fadeOut(100, function () {
currentContent.fadeIn(100);
});
}
}
return false;
});
});
Any help will be highly appreciated.
According to the docs for jQuery.Get:
Due to browser security restrictions, most "Ajax" requests are subject to the same origin policy; the request can not successfully retrieve data from a different domain, subdomain, or protocol.
If you're after JSON responses, then you should consider using the JSONP option that has been rolled into the GetJSON method.
There are a couple of people out there who have however provided some workarounds for the Get limitation:
The jQuery Cross Domain Ajax Guide
Cross Domain Requests with jQuery