Javascript - run function in iFrame after parent window has loaded - javascript

Is there a way to know if parent window has loaded from within iframe?
I want to run a function which is in iFrame but I need to run it after all the parent windows are loaded and the event listener will be inside the iframe.
I tried the following but it runs before parent windows are loaded.
window.addEventListener('load', function () {
alert("It's loaded!")
});

One way would be to add the iframe dynamically:
parent:
window.addEventListener('load', function () {
var iframe = document.createElement('iframe');
iframe.src = "https://www.example.com";
document.body.appendChild(iframe);
});
iframe:
window.addEventListener('load', function () {
alert('hello world!');
//doSomethingUseful();
}
This way, you could be certain that they will load in a specific order. However, as they'd be loading in series, the increase in total page load time could become noticeable.
Alternatively, you could use this approach. This one may not work as is, if one page happens to finish loading before the other. If you do opt for this approach, it may be necessary to communicate in both directions so that the first page to load finds out when the second page has loaded. That may look like this:
parent:
newEvent();
window.document.addEventListener('myCustomEventI', newEvent, false);
function newEvent() {
var data = { loaded: true }
var event = new CustomEvent('myCustomEventP', { detail: data })
window.parent.document.dispatchEvent(event);
}
iframe:
newEvent();
window.document.addEventListener('myCustomEventP', handleEvent, false);
function newEvent() {
var data = { loaded: true }
var event = new CustomEvent('myCustomEventI', { detail: data })
window.parent.document.dispatchEvent(event);
}
function handleEvent(e) {
alert('both loaded!');
//doSomethingUseful();
}

Related

JavaScript is not applying on page

I have below code that I have written in JavaScript and the script is referenced on the webpage. When the page loads, a call JavaScript happens and the logic's action should be rendered on the webpage.
Right now the script is firing on the webpage, but the action is not getting rendered on the webpage. However, if I execute the script on page console, changes happen.
<script>
function bannerLoad() {
var delayAddOn = setInterval(function() {
if ($(".add-ons").hasClass("current")) {
if ($('.addons-sidebar.clearfix img').length < 1) {
$(".addons-container :last").append($('<img>', {
class: 'img-responsive',
src: 'https://www.abc.in/content/dam/abc/6e-website/banner/target/2018/06/abc.png'
}));
}
clearInterval(delayAddOn);
}
}, 100);
};
window.onload = function() {
bannerLoad();
};
window.onclick = function() {
bannerLoad();
};
</script>
Can anyone check if there is any issue?
You need to call the script when the page is fully loaded, else the function will be called and can't find the DOM elements.
You should wrap your code inside the ready function:
<script>
//OPEN THE READY FUNCTION
$(function(){
bannerLoad(); //Call of your function when the page is fully loaded
$(window).click(bannerLoad);
});
//CLOSE THE READY FUNCTION
function bannerLoad() {
var delayAddOn = setInterval(function()
{
if($(".add-ons").hasClass("current"))
{
if($('.addons-sidebar.clearfix img').length < 1)
{
$(".addons-container :last").append($('<img>',{class:'img-responsive',src:'https://www.abc.in/content/dam/abc/6e-website/banner/target/2018/06/abc.png'}));
}
clearInterval(delayAddOn);
}
}, 100);
};
</script>
A page can't be manipulated safely until the document is "ready." jQuery detects this state of readiness for you. Code included inside $( document ).ready() will only run once the page Document Object Model (DOM) is ready for JavaScript code to execute. Code included inside $( window ).on( "load", function() { ... }) will run once the entire page , not just the DOM, is ready.
// A $( document ).ready() block.
$( document ).ready(function() {
console.log( "ready!" );
bannerLoad();
$(window).click(bannerLoad);
});
function bannerLoad() {
if($(".add-ons").hasClass("current"))
{
if($('.addons-sidebar.clearfix img').length < 1)
{
$(".addons-container :last").append($('<img>',{class:'img-responsive',src:'https://www.abc.in/content/dam/abc/6e-website/banner/target/2018/06/abc.png'}));
}
clearInterval(delayAddOn);
}
}, 100);
};
Your script has some little issues. I will try to evaluate them.
As bannerLoad is a function you don't need a ; at the end. Not an issue, just a hint.
As told before, bannerLoad is a function. So why would you wrap the function again in a function for your events? Just pass the function name directly, like window.click = bannerLoad;. Note that there are no bracers at the end, you just pass the name.
You function will always create a new delayAddOn variable with a new interval. So every time you click, another interval will be started and run in background. If you will do it like this, you need to put the variable on the outside of your function, to keep only one interval running at a time.
There is nothing wrong with using onload instead of a ready state from jQuery. But this belongs to you page setup and what you do. It would be more safe to rely on a ready state here, as told by others before. Because you already have a function, you could use it directly by $(bannerLoad);.
var delayAddOn;
function bannerLoad() {
delayAddOn = setInterval(function() {
if ($('.add-ons').hasClass('current')) {
if ($('.addons-sidebar.clearfix img').length < 1) {
$('.addons-container :last').append($('<img>', {
class: 'img-responsive',
src: 'https://www.abc.in/content/dam/abc/6e-website/banner/target/2018/06/abc.png'
}));
}
clearInterval(delayAddOn);
}
}, 100);
}
$(bannerLoad);
window.onclick = bannerLoad;

Detect when the iframe inside another iframe is loaded from third(parent) iframe

Brainf... with iframes.
So, current page structure:
page -> main_iframe -> iframe1 -> iframe2
The scripts are executed in "main_iframe".
This "main_iframe" has "iframe1" and "iframe1" has "iframe2" inside:
I want to change width=100% of iframe2 which is inside iframe1, which is inside main_iframe
SO i am executing following js at "main_iframe" and it works:
document.querySelector('#iframe1').onload = function() {
setTimeout(function(){
document.querySelector('#iframe1').contentDocument.querySelectorAll("iframe")[0].style.width = '100%';
}, 3000);
}
But how to remove timeout timer so it will render as soon as iframe2 has loaded?
I've tried following:
document.querySelector('#iframe1').onload = function() {
document.querySelector('#iframe1').contentDocument.querySelectorAll("iframe")[0].onload = function () {
document.querySelector('#iframe1').contentDocument.querySelectorAll("iframe")[0].style.width = '100%';
}
};
But without luck, nothing is happening after onload fires.
You can reference the outer iframe with this inside the load event handler, which makes it a bit tidier. This should do what you need...
document.querySelector("#iframe1").addEventListener("load", function() {
this.contentDocument.querySelector("iframe").style.width = "100%";
});

javascript postmessage doesn't work

i have 2 sites, and i want to use javascriptpostMessage between them.
on my main site i write the following code in an emty html file :
<html>
<script>
window.addEventListener("message",listener_,false);
function listener_(event) {
console.log('ok!');
<html>
<script>
window.addEventListener("message",listener_,false);
function listener_(event) {
console.log('ok!');
//console.log(event);
//console.log(event.data);
}
</script>
</html>
and in other site that i want to call the postmessage from the i write the following code:
<script type="text/javascript">
window.onload = function() {
testfunction();
};
function testfunction(){
var childWin = window.open("http://my-main-site.com/indexjava2.html","child");
childWin = postMessage('message','*');
console.log(TipaltiIframeInfo.height);
}
</script>
but it doesn't work after a lot of tries. i mean
console.log('ok!'); or console.log(event); console.log(event.data);
doesn't trigger on console of main site,
what to do?
thanks
Aside from the fact that you've got a <script> and an <html> tag in the middle of your code for the receiving page and you're defining and adding the event listener twice, you're also not use postMessage correctly. Instead of this:
childWin = postMessage('message', '*');
...it should be this:
childWin.postMessage('message', '*');
If you want to learn more about postMessage, read this.
The other issue is that the message won't be received by the newly-opened page unless the page is opened before the message is sent. You're trying to send the message immediately after opening the new page, and the message is reaching the new page before the event listener is added. You could get around this with a setTimeout, but if the new page takes longer to load then this might also be unreliable.
// This is what NOT to do:
setTimeout(function() {
childWin.postMessage('message', '*');
}, 1000);
Instead, the better way is for the child page to tell the parent page when it's loaded. Then the parent and child can communicate reliably.
Here is the full corrected code, sending page first:
<script>
var childWin;
window.addEventListener('message', messageListener, false);
function messageListener(event) {
if(event.data == 'ready') {
childWin.postMessage('hello second page', '*');
}
}
window.onload = function() {
childWin = window.open('http://my-main-site.com/indexjava2.htm', 'child');
};
</script>
And the receiving page:
<script>
window.addEventListener('message', messageListener, false);
function messageListener(event) {
console.log(event.data);
}
window.opener.postMessage('ready','*');
</script>

How to pass data from content-script to page-level?

I'm injecting all my js code to front page, but it needs pictures for ui and stuff, that can be imported only with the help of chrome.extension.getUrl and can be called only from content-script, so I've found tons of advices how to pass data to content page, and nothing of about how pass data back, is it possible at all?
My code now looks like this:
my js code, that will be injected with other code:
var Content = {};
$(document).contentReady = function(content) {
Content = content;
$(document).ready(function () {/*cool stuff here, that require content*/});
}
var event = new CustomEvent('LoadContent');
window.dispatchEvent(event);
content-script:
document.querySelector('head').appendChild(jsCode);
window.addEventListener("LoadContent", function(evt) {
var content =
{
data: "url(" + chrome.extension.getURL('content.smth') + ")"
};
document.contentReady(content);
}, false);
And, obviously, I get document.contentReady is not a function
But declaring function in document was the only(!) advice of about how to pass data back from content-script after about 2 hours of googling.
Nothing stops you from making the CustomEvent-based communication bi-directional, and it can pass data with detail property:
// Page script
window.addEventListener('RecieveContent', function(evt) {
// do something cool with evt.detail
});
var event = new CustomEvent('LoadContent');
window.dispatchEvent(event);
// Content script
window.addEventListener('LoadContent', function(evt) {
content = /* ... */
var event = new CustomEvent('RecieveContent', {detail: content});
window.dispatchEvent(event);
});
A more in-depth answer can be found here.
However, you should ask yourself whether you even need the page-level script to query for data, since you fully control when it's injected. You can use uni-directional approach after you make sure the code has executed:
// Page script
window.addEventListener('RecieveContent', function(evt) {
// do something cool with evt.detail
});
// Content script
jsCode.onload = function() {
// This fires after the page script finishes executing
content = /* ... */
var event = new CustomEvent('RecieveContent', {detail: content});
window.dispatchEvent(event);
}
document.querySelector('head').appendChild(jsCode);
You can pass JS data to the page by creating a new script tag. For example:
function injectScript(code) {
var body = document.getElementsByTagName('body')[0];
var s = document.createElement('script');
s.innerHTML = code;
body.appendChild(s);
}
injectScript('var foo = 2;');
So for your particular example, you should be able to do:
injectScript('document.contentReady({data: url(' + blahblah + '})');
Not pretty (what is when you're working with overwriting content scripts?) but it works.
Content Scripts do not share window object with normal scripts on the page. Both of them work on different context.
In your case, you are registering an event listener on window and listening for the event on other context (window). Hence, your event listener will never be called.
However, there is one alternative approach I can see to communicate between content script and normal script is by using MutationObserver.
Idea
Define a node with some Id under which you will create subnodes corresponding to an event.
Register Mustation Observer in your script.
From content script, add the nodes with data as data-* api.
Implementation Example
Content Script
var submitEvent = function(category, action, label) {
var eventObserverPlaceholder = document.getElementById('events-observer-placeholder'),
$eventEl = $('<span></span>').attr({
'data-category': category,
'data-action': action,
'data-label': label
});
eventObserverPlaceholder.appendChild($eventEl.get(0));
};
Normal Script for registering Mutation Observer:
RQ.Methods.addObserverForEvents = function(targetNode) {
var observer = new MutationObserver(RQ.Methods.handleMutationList);
// Notify me when a new child is added
var observerConfig = {
attributes: false,
childList: true,
characterData: false
};
observer.observe(targetNode, observerConfig);
return observer;
};
RQ.mutationObserver = RQ.Methods.addObserverForEvents(document.getElementById('events-observer-placeholder'));
Links
https://davidwalsh.name/mutationobserver-api
https://developer.mozilla.org/en/docs/Web/API/MutationObserver
Working Example:
I have used the same approach in Requestly Chrome Extension for submitting events to Google Analytics.
Content Script: https://github.com/requestly/chrome-extension/blob/master/src/Shared/utils.js#L26
Normal Script: https://github.com/requestly/web/blob/gh-pages/js/scripts/tracker.js#L35

How to determine if content in popup window is loaded

I need to set some contextData for a popup window from its parent. I try this:
var some contextData = {};
$(function() {
$('#clickme').click(function() {
var w = window.open('http://jsfiddle.net');
w.contextData = contextData
//w.context data is null in the popup after the page loads - seems to get overwritten/deleted
});
});
It doesn't work, so my next thought, wait until content is loaded
var some contextData = {};
$(function() {
$('#clickme').click(function() {
var w = window.open('http://jsfiddle.net');
w.onload = function() {
//Never Fires
w.contextData = contextData;
}
});
});
See this fiddle. My onload method never fires.
This works:
var some contextData = {};
$(function() {
$('#clickme').click(function() {
var w = window.open('http://jsfiddle.net');
setTimeout(function(){
if(w.someVariableSetByThePageBeingLoaded) {
w.contextData = contextData;
}
else{
setTimeout(arguments.callee, 1);
}
}, 1);
});
});
But has obvious elegance problems (but is the current work around).
I know you can go the other way (have the popup call back to a method on the opener/parent, but this forces me to maintain some way of looking up context (and I have to pass the key to the context to the popup in the query string). The current method lets me capture the context in a closure, making my popup a much more reusable piece of code.
I am not trying to do this cross domain - both the parent and popup are in the same domain, although the parent is an iframe (hard to test with jsfiddle).
Suggestions?
If you are doing this with an iframe try it this way
HTML
<button id="clickme">Click Me</button>
<iframe id="framer"></iframe>
​
Javascript
$(function() {
$('#clickme').click(function() {
$("#framer").attr("src","http://jsfiddle.net");
$("#framer")[0].onload = function(){
alert('loaded');
};
});
});​
I updated your jsfiddle http://jsfiddle.net/HNvn3/2/
EDIT
Since the above is completely wrong this might point you in the right direction but it needs to be tried in the real environment to see if it works.
The global variable frames should be set and if you
window.open("http://jsfiddle.net","child_window");
frames["child_window"] might refer to the other window
I got javascript access errors when trying it in jsfiddle - so this might be the right track
EDIT2
Trying out on my local dev box I was able to make this work
var w = window.open("http://localhost");
w.window.onload = function(){
alert("here");
};
the alert() happened in the parent window

Categories

Resources