I have been playing with HTML postMessage and managed to pass messages between my web page and the popup window it opens.
index.html:
var popup;
window.onload = function () {
console.log("window.onload");
var btn = document.getElementById('signin');
btn.addEventListener('click', signin);
};
var pollTimer;
function signin(e) {
console.log("window.onload - signin");
popup = window.open("http://www.olcayertas.com/testqa/redirect.html", "_blank", "toolbar=yes, scrollbars=yes, resizable=yes, top=10, left=700, width=600, height=600");
pollTimer = window.setInterval(function () {
if (popup.closed !== false) { // !== is required for compatibility with Opera
window.clearInterval(pollTimer);
document.getElementById('signin').style.visibility = 'hidden';
}
e.preventDefault();
popup.postMessage('message', 'http://www.olcayertas.com/testqa/index.html');
console.log("sendMessage - Message sent!!!");
}, 500);
}
function receiveMessage(event){
window.clearInterval(pollTimer);
}
window.addEventListener("message", receiveMessage, false);
popup.html:
console.log("script");
window.onload = first();
var messageEle;
function receiveMessage(e) {
console.log("receiveMessage");
messageEle = document.getElementById('message');
messageEle.innerHTML = "Message Received: " + e.data;
e.source.postMessage("Mesaj alındı!", e.origin);
}
function first() {
console.log("first");
window.addEventListener('message', receiveMessage);
}
What I need is redirect the user to an other page by setting top.location parameter in my popup window and pass the e.source parameter to this page so it can also send message to index.html. Since I do not know how the e parameter of the receiveMessage(e) function passed I am stuck.
How can I do that?
Thanks in advance.
Related
I have a script inside a child google tag manager account which is firing on submission of an iframe form that is generated via javascript, this should be picked up via the parent google tag manager account through the dataLayer. I am following this tutorial: https://measureschool.com/iframe-tracking/
However it's not going into the dataLayer on the parent - hence the tag isn't firing. Can anyone help?
To confirm
Child tag manager account fires at: https://new.bokuntest.com/ - the iframe form URL
Parent tag manager account fires at: https://bokun.io/
URL of page with embedded iframe form: https://bokun.io/copy-of-free-trial
postMessage tag firing in child:
<script>
try {
var postObject = JSON.stringify({
event: 'iframeFormSubmit',
form: 'Registration'
});
parent.postMessage(postObject, 'https://new.bokuntest.com');
} catch(e) {
window.console && window.console.log(e);
}
</script>
Listener tag on parent (trigger = fire on all pages):
<script type="text/javascript">
(function(window) {
addEvent(window, 'message', function(message) {
try{
var data = JSON.parse(message.data);
var dataLayer = window.dataLayer || (window.dataLayer = []);
if (data.event) {
dataLayer.push({
'event': data.event,
'postMessageData': data
});
}
}catch(e){}
});
// Cross-browser event listener
function addEvent(el, evt, fn) {
if (el.addEventListener) {
el.addEventListener(evt, fn);
} else if (el.attachEvent) {
el.attachEvent('on' + evt, function(evt) {
fn.call(el, evt);
});
} else if (typeof el['on' + evt] === 'undefined' || el['on' + evt] === null) {
el['on' + evt] = function(evt) {
fn.call(el, evt);
};
}
}
})(window);
</script>
Trigger on parent (it's this that doesn't seem to be happening):
Form submission tag on parent:
Can anyone figure out what I've missed?
window.parent.postMessage(postObject, 'https://new.bokuntest.com');
try {
var postObject = JSON.stringify({
event: 'iframeFormSubmit',
form: 'Registration'
});
window.parent.postMessage(postObject, 'https://new.bokuntest.com');
} catch(e) {
window.console && window.console.log(e);
}
Hey guys I need just a little bit of help with this.
So I have modal boxes hiding on my page and when I click on them using the video platform VERSE they work perfectly.
My questions is: How can I call the same modal boxes if I wan to call them from a regular link or button on the page.
Here is the sample:
http://digitalfeast.com/clients/nccv/ncc-verse.html
Here is my Javascript code:
(function() {
(function() {
window.onload = function() {
var frame = document.getElementsByName("verse-iframe")[0].contentWindow;
// Variables below (i.e. "menu-1") reference div id from your markup
function receiveMessage(event) {
var data = (typeof event.data === "String") ? JSON.parse(event.data) : event
var modalWindow1 = document.getElementById("ruben-1");
var modalWindow2 = document.getElementById("ruben-2");
var modalWindow3 = document.getElementById("menu-3");
var modalWindow4 = document.getElementById("menu-4");
// Variables below (i.e. "menu-1") reference the unique callback names entered for your hotspots in the Verse editor
if (data.data["identifier"] === "ruben-1") {
modalWindow1.style.display = "block";
}
if (data.data["identifier"] === "ruben-2") {
modalWindow2.style.display = "block";
}
if (data.data["identifier"] === "menu-3") {
modalWindow3.style.display = "block";
}
if (data.data["identifier"] === "menu-4") {
modalWindow4.style.display = "block";
}
}
var closeBtns = document.getElementsByClassName("modal-close");
for (var i = 0; i < closeBtns.length; i++) {
var btn = closeBtns[i];
btn.onclick = function (event) {
event.target.parentNode.parentNode.style.display = "none";
frame.postMessage({action: "play"}, "*");
};
}
window.addEventListener('message', receiveMessage);
var frame = document.getElementsByName("verse-iframe")[0].contentWindow;
};
}());
}());
Given your code, all you need to do is send the window a message using the Messaging API inside your button click handler.
Your event listener will then execute the receiveMessage function and open your model for ruben-1.
window.onload = () => {
document.querySelector('[data-modal="ruben-1"]').addEventListener("click", (e) => {
let postData = {
identifier: e.target.dataset.modal
};
window.postMessage(postData, "*");
});
window.addEventListener('message', m => {
alert(m.data.identifier);
});
}
<button data-modal="ruben-1">Ruben-1 Video</button>
I have a published captivate html file that is loaded into an iframe of another html. I cannot communicate between the two, not even with localStorage. Can anyone tell me what I'm missing?
Parent html
var everythingLoaded = setInterval(function () {
if (/loaded|complete/.test(document.readyState)) {
clearInterval(everythingLoaded);
init();
}
}, 10);
function init() {
ScormProcessInitialize();
var studentID = ScormProcessGetValue("cmi.core.student_id");
var student_name = ScormProcessGetValue ("cmi.core.student_name");
var nameArraya = student_name.split(" ");
var nameArrayb = nameArraya[1].split(",");
var studentNumber = nameArrayb[0];
ScormProcessSetValue("cmi.core.lesson_status", "incomplete");
localStorage.setItem("_studentNumber", studentNumber);
alert("Student Number: " + studentNumber + " Student Mame: " + student_name);
setTimeout(function () {
document.getElementById("iFrame_a").innerHTML = "<iframe name='iframe_1' id='frame_1' src='//somepath.com/sandbox/somecourse/index.html' frameborder='0' width='1000px' height='605px'></iframe>";
}, 250);
}
function sendComplete() {
alert("Send from index start!");
ScormProcessSetValue("cmi.core.lesson_status", "completed");
alert("send status: Completed");
}
window.onbeforeunload = function (){
cpInfoCurrentSlide = localStorage.getItem("_cpInfoCurrentSlide")
alert(cpInfoCurrentSlide);
if(cpInfoCurrentSlide >= 40)
{
alert("onbeforeunload called: " + cpInfoCurrentSlide )
ScormProcessSetValue("cmi.core.lesson_status", "completed");
}
}
iframe code snippet
localStorage.setItem("_cpInfoCurrentSlide", cpInfoCurrentSlide);
I believe your problem is with onbeforeunload. As I remember captivate packages clobber any functions associated with onbeforeunload in the parent frame when they load.
Try this instead, override your SCORM api setvalue method:
var oldLMSSetValue = window.API.LMSSetValue;
window.API.LMSSetValue = function(key, value){
if(key === 'cmi.core.lesson_status' && value === 'completed'){
//do your stuff here
cpInfoCurrentSlide = localStorage.getItem("_cpInfoCurrentSlide")
alert(cpInfoCurrentSlide);
}
//call the original scorm api function so that it runs as expected.
oldLMSSetValue(key,value);
};
edit: this code would go in the parent window, not the iframe.
I'm using the iframe YouTube API and I want to track events, for example, sending data to google analytics, when user start and stop video.
<iframe src="https://www.youtube.com/embed/DjB1OvEYMhY"></iframe>
I looked https://developers.google.com/youtube/iframe_api_reference?csw=1 and did not find an example how to do that. The example creates iframe and defines onReady and onStateChange as well. How would I do same when I've only iframe on page?
This example listens to every play/pause action the user makes, using onPlayerStateChange with its different states, and prints (records) them.
However, you need to create your own record function to do whatever you want with this data.
You also need an ID on your iframe (#player in this case) and to add ?enablejsapi=1 at the end of its URL. And of course, make sure to include the Youtube iframe API.
Note
It's important to declare the API after your code, because it calls onYouTubeIframeAPIReady when it's ready.
<!DOCTYPE html>
<html>
<body>
<iframe id="player" src="https://www.youtube.com/embed/DjB1OvEYMhY?enablejsapi=1"></iframe>
<h5>Record of user actions:</h5>
<script>
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player( 'player', {
events: { 'onStateChange': onPlayerStateChange }
});
}
function onPlayerStateChange(event) {
switch(event.data) {
case 0:
record('video ended');
break;
case 1:
record('video playing from '+player.getCurrentTime());
break;
case 2:
record('video paused at '+player.getCurrentTime());
}
}
function record(str){
var p = document.createElement("p");
p.appendChild(document.createTextNode(str));
document.body.appendChild(p);
}
</script>
<script src="https://www.youtube.com/iframe_api"></script>
</body>
</html>
JS Fiddle Demo
Here is a version that doesn't use Youtubes iframe API script. The only drawback is that the iframe API might change.
<iframe id="player" src="https://www.youtube.com/embed/dQw4w9WgXcQ?enablejsapi=1"></iframe>
var addYoutubeEventListener = (function() {
var callbacks = [];
var iframeId = 0;
return function (iframe, callback) {
// init message listener that will receive messages from youtube iframes
if(iframeId === 0) {
window.addEventListener("message", function (e) {
if(e.origin !== "https://www.youtube.com" || e.data === undefined) return;
try {
var data = JSON.parse(e.data);
if(data.event !== 'onStateChange') return;
var callback = callbacks[data.id];
callback(data);
}
catch(e) {}
});
}
// store callback
iframeId++;
callbacks[iframeId] = callback;
var currentFrameId = iframeId;
// sendMessage to frame to start receiving messages
iframe.addEventListener("load", function () {
var message = JSON.stringify({
event: 'listening',
id: currentFrameId,
channel: 'widget'
});
iframe.contentWindow.postMessage(message, 'https://www.youtube.com');
message = JSON.stringify({
event: "command",
func: "addEventListener",
args: ["onStateChange"],
id: currentFrameId,
channel: "widget"
});
iframe.contentWindow.postMessage(message, 'https://www.youtube.com');
});
}
})();
addYoutubeEventListener(document.getElementById("player"), function(e) {
switch(e.info) {
case 1:
// playing
break;
case 0:
// ended
break;
}
});
Sometimes the event load is not enough to ensure that the document inside the iframe is ready. If the iframe is in a different domain it is not possible to subscribe to see when it is ready.
A possible workaround is to record when an event is received from the iframe, if after subscribing no event was received try again:
var addYoutubeEventListener = (function() {
var callbacks = [];
var iframeId = 0;
var subscribed = [];
return function (iframe, callback) {
// init message listener that will receive messages from youtube iframes
if(iframeId === 0) {
window.addEventListener("message", function (e) {
if(e.origin !== "https://www.youtube.com" || e.data === undefined) return;
try {
var data = JSON.parse(e.data);
subscribed[data.id] = true;
if(data.event !== 'onStateChange') return;
var callback = callbacks[data.id];
callback(data);
}
catch(e) {}
}, true);
}
// store callback
iframeId++;
callbacks[iframeId] = callback;
subscribed[iframeId] = false;
var currentFrameId = iframeId;
//console.log("adding event listener to iframe id " + iframeId);
// sendMessage to frame to start receiving messages
iframe.addEventListener("load", function () {
var tries = 0;
var checkSubscribed = function()
{
if (subscribed[currentFrameId]) {
//console.log("subscribed succesfully " + currentFrameId)
}
else
{
tries++;
//console.log("Try again " + currentFrameId + " (" + tries + ")");
if (tries < 100) {
doSubscribe();
}
else
{
console.log("Unable to subscribe" + currentFrameId );
}
}
}
var doSubscribe = function()
{
var message = JSON.stringify({
event: 'listening',
id: currentFrameId,
channel: 'widget'
});
iframe.contentWindow.postMessage(message, 'https://www.youtube.com');
message = JSON.stringify({
event: "command",
func: "addEventListener",
args: ["onStateChange"],
id: currentFrameId,
channel: "widget"
});
iframe.contentWindow.postMessage(message, 'https://www.youtube.com');
setTimeout(checkSubscribed, 100);
};
doSubscribe();
}, true);
}
})();
I have a content script that runs from my chrome extension.
this script injects an iframe to the body of the current page.
i want to have the possibility to close the iframe from within the iframe.
how do i do this?
when i searched this issue on the web, almost each solution uses the window.parent.document property which for some reason is undefined in my case. any ideas?
EDIT - Code Sample:
in the HTML of the iframe:
<script type="text/javascript">
function frameClose() {
var windowFrames = window.parent.frames;
for (var i = 0; i < windowFrames.length; i++) {
var aFrame = windowFrames[i];
if (aFrame.name == 'myFrame') {
alert('in frame');
// WHAT TO DO HERE?
// window.parent.document is undefined
// aFrame.parentNode.removeChild(aFrame); - THIS DOES NOT WORK ALSO
break;
}
}
}
</script>
this is how i inject the iframe:
Extension.js
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(null, {
file : "/js/PushIFrame.js"
}, function() {
if (chrome.extension.lastError) {
}
});
});
and in PushIFrame.js i have:
chrome.extension.sendMessage({
action: "pushFrame",
source: pushIframe(document)
});
function pushIframe(document) {
var existingFrame = document.getElementById('bmarkFrame');
if (existingFrame == null) {
var temp = document.createElement('iframe');
temp.id = 'myFrame';
temp.name = 'myFrame';
temp.setAttribute('scrolling', 'no');
temp.setAttribute('allowtransparency', 'true');
temp.style.border = 'none';
temp.style.height = '100%';
temp.style.width = '100%';
temp.style.position = 'fixed';
temp.style.zIndex = 99999999;
temp.style.top = 0;
temp.style.left = 0;
temp.style.display = 'block';
temp.src = 'https://www.mysite.com/';
document.body.appendChild(temp);
}
else {
existingFrame.style.display = 'block';
}
}
Let the content script (say PushIframe.js) bind a message event to the main frame. Then, whenever you want to hide the iframe, call parent.postMessage to notify the main frame. This message is received by the content script, from where you can hide the frame (as defined in your function pushIframe).
// PushIframe.js:
addEventListener('message', function(ev) {
if (ev.data === 'closeIframe') {
pushIframe(document); // Your code
}
});
// Iframe:
<script>
function frameClose() {
parent.postMessage('closeIframe', '*');
}
</script>