Using postMessage() isn't being received - javascript

I'm sure it's just a problem with my syntax but I am trying to send a variable to an iframe (for colorbox to use). For the time being I am accepting any domains on both ends (just to get it to work). Here is the js for the sending page:
$(document).ready(function() {
if(window.location.hash) {
var urlimage = window.location.hash.substring(1);
targetiframe = document.getElementById('wbgallery').contentWindow;
targetiframe.postMessage(urlimage, "*");
console.log(urlimage);
}
});
And here is the receiving page:
$(document).ready(function() {
window.addEventListener('message',receiveMessage);
console.log(event);
function receiveMessage(event) {
if (origin !== "*")
return;
inbound = event.data;
console.log(inbound);
}
});
I see the console log for urlimage and can see an event but nothing for inbound. I'm using Mozilla's explanation to try and work it all out.

You're sending the message before the page in the iframe has loaded, so the message listener hasn't been established yet.
You can have the iframe send a message to the parent when it's ready, and then send the message after this.
Parent code:
$(document).ready(function() {
if (window.location.hash) {
var urlimage = window.location.hash.substring(1);
var targetiframe = document.getElementById('wbgallery').contentWindow;
$(window).on("message", function(e) {
targetiframe.postMessage(urlimage, "*");
console.log(urlimage);
});
}
});
Iframe code:
$(document).ready(function() {
$(window).on('message', function(event) {
inbound = event.data;
console.log(inbound);
});
window.parent.postMessage("ready", "*");
});

Related

Cordova - ExecuteScript callback when i click submit button

I'm currently working with inAppBrowser to open an external web hosted app, I used executeScript and loaded external js to interact with the DOM, is there a way to fire the callback when I click on the submit button?
or a way to access the dom without executeScript?
What I need to do is fire the callback on click event because I need to pass the localStorage before changing the domain.
index.js
onDeviceReady: function() {
if (navigator.connection.type == Connection.NONE) {
navigator.notification.alert('An internet connection is required to continue');
} else {
var options = "location=no,hidden=no";
var ref = cordova.InAppBrowser.open('extSite', '_blank', options);
ref.addEventListener('loadstop', function(event){
console.log("il link e' " +event.url);
if(event.url.startsWith("extSite")){
ref.executeScript({
file:'extfile'
});
}
});
}
}
extfile.js
document.getElementById('LoginButton').addEventListener('click', function(){
if (document.getElementsByName("RememberLogin")[0].checked==true){
var usr = document.getElementsByName('Username')[0].value;
var psw = document.getElementsByName('Password')[0].value;
if ((usr!="") && (psw!="")){
localStorage.setItem('Username', usr);
localStorage.setItem('Password', psw);
alert('data saved');
}
}
else {
localStorage.removeItem("Username");
localStorage.removeItem("Password");
}
});

Why my window postMessage isn't working ? Not using iframe

So I've been trying several things in my project to send cross-domain variables.
I have a button that I click, and opens a new browser tab.
$('#btn').on('click', function(e) {
var popup = window.open("other-domain.html", "_blank");
window.popup.onload = function() {
popup.postMessage(variableToSend, '*');
}
// I even tried doing this directly, without the onload
popup.postMessage(variableToSend, '*');
});
From my other domain I do this:
(function($) {
var listener = function(event) {
console.log(event.data);
}
var setupEvents = function() {
if(window.addEventListener) {
window.addEventListener("message", listener, false);
}else{
window.attachEvent("onmessage", listener);
}
}
setupEvents();
})(jQuery);
I never receive anything from my parent window, so never gets inside listener function.
Do you know what could be wrong ? Been fighting with this for 2 days already.
Thanks in advance,
ADDED:
I tried doing this from the other domain window (children)
window.parent.postMessage('Hi!', '*');
And it receives the message correctly. Looks like I'm missing something, maybe a Timeout somewhere?

Change a parent window's URL using history pushState when link in Iframe is clicked

I have a page that has a header and sidebar with a right content panel that is an Iframe.
In a page loaded into the right content panel, I am trying to have a clicked link update the Browser URL in the parent window to the URL of the new page that is loaded into the Iframe.
I do not want the actual parent window to reload the URL but simply to update the URL in the address bar.
Something like:
window.history.pushState('obj', 'newtitle', '/bookmarks/list/');
Is this possible from an Iframe?
I was able to accomplish updating the parent windows URL in the address bar using history.pushState by sending the new URL to the parent from the child Iframe window using postMessage and on the parent window listening for this event.
WHen the parent receives the child iframes postMessage event, it updates the URL with pushSTate using the URL passed in that message.
Child Iframe
<script>
// Detect if this page is loaded inside an Iframe window
function inIframe() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
// Detect if the CTRL key is pressed to be used when CTRL+Clicking a link
$(document).keydown(function(event){
if(event.which=="17")
cntrlIsPressed = true;
});
$(document).keyup(function(){
cntrlIsPressed = false;
});
var cntrlIsPressed = false;
// check if page is loaded inside an Iframe?
if(inIframe()){
// is the CTRL key pressed?
if(cntrlIsPressed){
// CTRL key is pressed, so link will open in a new tab/window so no need to append the URL of the link
}else{
// click even on links that are clicked without the CTRL key pressed
$('a').on('click', function() {
// is this link local on the same domain as this page is?
if( window.location.hostname === this.hostname ) {
// new URL with ?sidebar=no appended to the URL of local links that are clicked on inside of an iframe
var linkUrl = $(this).attr('href');
var noSidebarUrl = $(this).attr('href')+'?sidebar=no';
// send URL to parent window
parent.window.postMessage('message-for-parent=' +linkUrl , '*');
alert('load URL with no sidebar: '+noSidebarUrl+' and update URL in arent window to: '+linkUrl);
// load Iframe with clicked on URL content
//document.location.href = url;
//return false;
}
});
}
}
</script>
Parent window
<script>
// parent_on_message(e) will handle the reception of postMessages (a.k.a. cross-document messaging or XDM).
function parent_on_message(e) {
// You really should check origin for security reasons
// https://developer.mozilla.org/en-US/docs/DOM/window.postMessage#Security_concerns
//if (e.origin.search(/^http[s]?:\/\/.*\.localhost/) != -1
// && !($.browser.msie && $.browser.version <= 7)) {
var returned_pair = e.data.split('=');
if (returned_pair.length != 2){
return;
}
if (returned_pair[0] === 'message-for-parent') {
alert(returned_pair[1]);
window.history.pushState('obj', 'newtitle', returned_pair[1]);
}else{
console.log("Parent received invalid message");
}
//}
}
jQuery(document).ready(function($) {
// Setup XDM listener (except for IE < 8)
if (!($.browser.msie && $.browser.version <= 7)) {
// Connect the parent_on_message(e) handler function to the receive postMessage event
if (window.addEventListener){
window.addEventListener("message", parent_on_message, false);
}else{
window.attachEvent("onmessage", parent_on_message);
}
}
});
</script>
Another solution using Window.postMessage().
Iframe:
/test
/test2
<script>
Array.from(document.querySelectorAll('a')).forEach(el => {
el.addEventListener('click', event => {
event.preventDefault();
window.parent.postMessage(this.href, '*');
});
});
</script>
Main page:
Current URL: <div id="current-url"></div>
<iframe src="iframe-url"></iframe>
<script>
const $currentUrl = document.querySelector('#current-url');
$currentUrl.textContent = location.href;
window.addEventListener('message', event => {
history.pushState(null, null, event.data);
$currentUrl.textContent = event.data;
});
</script>
See demo on JS Fiddle.

Chrome: message content-script on runtime.onInstalled

I'm the maker of an addon called BeautifyTumblr which changes the apperance of Tumblr.
I wish for my Chrome extension to automatically detect when it has been updated and display changelog to the user. I use an event page with the chrome.runtime.onInstalled.addListener hook to detect when an update has occured, retrieve the changelog from a text file in the extension.. this all works fine, then when I want to forward it to my content script via chrome.tabs.sendmessage it just wont work, nothing ever happens, no errors no nothing. I'm stumped.
Any help would be much appreciated!
Event Page:
chrome.runtime.onInstalled.addListener(function (details) {
"use strict";
if (details.reason === "install") {
} else if (details.reason === "update") {
var thisVersion = chrome.runtime.getManifest().version, xmlDom, xmlhttp;
xmlDom = null;
xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", chrome.extension.getURL("changelog.txt"), false);
xmlhttp.send(null);
xmlDom = xmlhttp.responseText;
chrome.tabs.query({'url' : 'http://www.tumblr.com/*'}, function (tabs) {
if (tabs.length > 0) {
var mTab = tabs[0].id;
chrome.tabs.update(mTab, {active: true});
setTimeout(chrome.tabs.sendMessage(mTab, {beautifyTumblrUpdate: xmlDom}), 500);
} else {
chrome.tabs.create({'url' : 'http://www.tumblr.com/dashboard'}, function (tab) {
setTimeout(chrome.tabs.sendMessage(tab.id, {beautifyTumblrUpdate: xmlDom}), 500);
});
}
});
}
});
Relevant code in Content Script:
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
"use strict";
window.alert('test');
if (request.beautifyTumblrUpdate) {
window.alert(request.beautifyTumblrUpdate);
} else if (request.beautifyTumblrInstall) {
window.alert(request.beautifyTumblrInstall);
}
}
);
I am also seeing the same thing. I am not a 100% sure but I think this happens because chrome shuts off connection between background page and "old" content scripts the moment the extension is updated. There's more info here in this bug : https://code.google.com/p/chromium/issues/detail?id=168263
simple, use the following code in background,
chrome.runtime.onInstalled.addListener(function(details){
if(details.reason == "install"){
chrome.tabs.create({ url: chrome.extension.getURL('welcome.html')});
}
});

How to communicate between iframe and the parent site?

The website in the iframe isn't located in the same domain, but both are mine, and I would like to communicate between the iframe and the parent site. Is it possible?
With different domains, it is not possible to call methods or access the iframe's content document directly.
You have to use cross-document messaging.
parent -> iframe
For example in the top window:
myIframe.contentWindow.postMessage('hello', '*');
and in the iframe:
window.onmessage = function(e) {
if (e.data == 'hello') {
alert('It works!');
}
};
iframe -> parent
For example in the top window:
window.onmessage = function(e) {
if (e.data == 'hello') {
alert('It works!');
}
};
and in the iframe:
window.top.postMessage('hello', '*')
In 2018 and modern browsers you can send a custom event from iframe to parent window.
iframe:
var data = { foo: 'bar' }
var event = new CustomEvent('myCustomEvent', { detail: data })
window.parent.document.dispatchEvent(event)
parent:
window.document.addEventListener('myCustomEvent', handleEvent, false)
function handleEvent(e) {
console.log(e.detail) // outputs: {foo: 'bar'}
}
PS: Of course, you can send events in opposite direction same way.
document.querySelector('#iframe_id').contentDocument.dispatchEvent(event)
This library supports HTML5 postMessage and legacy browsers with resize+hash https://github.com/ternarylabs/porthole
Edit: Now in 2014, IE6/7 usage is quite low, IE8 and above all support postMessage so I now suggest to just use that.
https://developer.mozilla.org/en-US/docs/Web/API/Window.postMessage
Use event.source.window.postMessage to send back to sender.
From Iframe
window.top.postMessage('I am Iframe', '*')
window.onmessage = (event) => {
if (event.data === 'GOT_YOU_IFRAME') {
console.log('Parent received successfully.')
}
}
Then from parent say back.
window.onmessage = (event) => {
event.source.window.postMessage('GOT_YOU_IFRAME', '*')
}
Updated:
postMessage should not work on cross domain, so the solution like this:
For example your website is: customer.com and your domain is my.com
You need to do like this
Create a js file (upload to CDN or your server) - my.com
Embed js file above to customer.com
Now from my.com, you can postMessage and above embed script can be received data from you.
the window.top property should be able to give what you need.
E.g.
alert(top.location.href)
See
http://cross-browser.com/talk/inter-frame_comm.html
After spending 2 days trying to get an iFrame posting messages back to the parent, a Vue application in my situation, I came across this excellent reference:
https://dev-bay.com/iframe-and-parent-window-postmessage-communication/
From the iframe to parent:
const parentWindow = window.parent;
class Message {
constructor(type, body) {
this.type = type;
this.body = body;
}
};
function sendMessage (windowObj, payload) {
if(windowObj) {
windowObj.postMessage(payload, "*");
}
};
//Then call appropriately:
sendMessage(parentWindow, new Message("button-click", "Show Stats Overlay"));
In the parent, my Vue application mounted life cycle event, but reference the link for your own requirement:
window.addEventListener("message", (e) => {
var data = e.data;
console.log("RECEIVED message from CHILD TO PARENT", data);
var type = data.type;
var body = data.body;
if(type === "button-click" && body) {
console.log("button-click RECEIVED FROM CHILD")
//Additional functionality ...
} else if (type === "text-msg" && body) {
console.log("TEXT MESSAGE RECEIVED FROM CHILD");
//Additional functionality ...
}
});
Please see reference for examples of communication from Parent to iFrame.
Hope this helps someone else.
You can also use
postMessage(message, '*');

Categories