Accessing the parent window's content from the child window - javascript

I am opening a new window when a button is clicked and then appending the content from this window to the window that has been opened, the jQuery code that I'm using is:
$(".printBtn").on("click", function () {
var w = window.open("", "Purchase Report", "width=800, height=1100");
var wi = $(window);
$(w.document.body).append(wi.find("#datatable_example"));
return false;
});
The problem is, a new window does open but the content from the parent window is not being appended to the newly opened window. I then tried to append wi.find("#datatable_example").html() but that didn't work either.
Can any one please have a look and tell me what I am doing wrong here?
UPDATE
Tried the following from the "duplicate question", but didn't work:
$(".printBtn").on("click", function () {
var w = window.open("", "Purchase Report", "width=800, height=1100");
$(w.document).ready(function () {
$(w.document.body).contents().append($(window).find("#datatable_example"));
});
return false;
});

The problem was, I was using var wi = $(window) instead of var wi = $(window.document). Here is the working code:
$(".printBtn").on("click", function () {
var w = window.open("", "Purchase Report", "width=800, height=1100");
var wi = $(window.document);
$(w.document.body).append(wi.find("#datatable_example"));
return false;
});

The root of your problem is same origin policy that won't let you observe load events for external domains. If you try this in developer console while browsing SO, everything's fine:
var child = window.open( 'http://stackoverflow.com' );
child.onload = function() {
alert( 'Popup loaded!' ); // Fired!
};
However if you try to open a page from other domain, it fails:
var child = window.open( 'http://stackexchange.com' );
child.onload = function() {
// It's not gonna be fired unless you run it from stackexchange.com.
alert( 'Popup loaded!' );
};
The same thing happens when you call window.open() or window.open( '' ) as it tries to load about:blank page which is out of your domain's scope and that's why your browser won't fire attached events (same for child.addEventListener( 'load' )). Also note that the protocol must match as well.
The workaround introduces setTimeout to run the callback in a separate JS thread, hopefully late enough to have DOM ready at that time:
var child = window.open();
setTimeout( function() {
var doc = child.document,
p = doc.createElement( 'p' );
p.innerHTML = 'Hello world!';
doc.body.appendChild( p );
} );
It works for me in latest Chrome, however different browsers sometimes do strange thing in such edge cases so you may need to tune this code with greater delay, sequential check or something else if it fails in your case.
So go ahead and tell us if it worked for you as I'm curious whether such workaround is fine for production code ;)

Related

Can't access document on new open window JS

I am trying to get some data from a webpage and pass it to new window and print it from there. Here is my code:
var print_btn;
let win;
document.getElementsByTagName('html')[0].innerHTML = document.getElementsByTagName('html')[0].innerHTML +"<button class=\"printbuton\">Print Button</button>";
print_btn = document.getElementsByClassName('printbuton')[0];
print_btn.onclick = function() {
console.log("check 1");
var WinPrint = window.open('', '', 'left=0,top=0,width=384,height=900,toolbar=0,scrollbars=0,status=0');
console.log("check 2");
WinPrint.document.write('Print this');
console.log("check 3");
WinPrint.document.write('Print that');
WinPrint.document.write('and this');
console.log("button pushed");
}
When I try this it opens the new window, but it stays empty and in console it logs only "check 1" and "check 2".
I tested that if i console.log(WinPrint) it shows in console, but if I do console.log(WinPrint.document) it doesn't show anything and the script stops there.
Your code will not work due to a (XSS [Cross Site Scripting]) Error. Firefox is preventing you from altering the content of a different domain.
Since you're opening a new window, you now have a different domain name (about:blank) than the one you started on.
There's a few ways to go about this, what comes quickly to mind would be to use the window.create API and use query params to pass the data to the new window:
function onCreated(windowInfo) {
console.log('SUCCESS');
}
function onError(error) {
console.log(`Error: ${error}`);
}
print_btn.addEventListener('click', event => {
var popupURL = browser.extension.getURL("popup/popup.html?line=blahblah&line2=loremipsum");
var creating = browser.windows.create({
url: popupURL,
type: "popup",
height: 200,
width: 200
});
creating.then(onCreated, onError);
});
Or you could do something more elaborate by opening using windows.create, then using runtime.sendmessage to pass messages back to the background script and then to the new window.
OR you could probably inject a popup into the page itself and do something similiar.

knockout.js 'ko' dissapears from page in event handler on Android Browser 4.0.4

I am observing very peculiar behaviour, when testing system on Samsung tablet some of links stopped working. To make it easier to debug I am using Chrome Developer tools emulator, when page is loaded I can call kncoukout.js global ko from console for debugging (like getting context for bound elements...) thing is when I am trying to debug from event handler ko vanishes.
Have a look at screenshot below.
I have been able to access ko when not in callback,
then I placed breakpoint in event handler and tried to access ko again on a click and it's not there anymore, and then it fails with error of ko not being there.
What is going on, how can ko just disappear?
Not that I think it matters (same happens in any event handler), but someone will ask for code that makes ko disappear.
var parseHrefBinding = function (link) {
var newHref = null;
var boundEl = $(link).closest('[data-bind]')[0];
if (boundEl) {
var data = ko.dataFor(boundEl);
if (data) {
var a = document.createElement('a');
a.setAttribute('data-bind', link.attributes['data-bind'].value);
ko.applyBindings(data, a);
newHref = a.href;
}
}
return newHref;
};
$(document).on("click", "a", function () {
var res = true;
var link = this;
var parent = $(link).parents('a')[0];
if (parent) {
// The default Android browser (V2-4.1) sets nested a[href] to their parent's href.
var href = parent.href === link.href ? parseHrefBinding(link) : link.href;
setTimeout(function () {
document.location.href = href;
}, 10);
res = false;
}
return res;
});
God this is epic, it turns out that to use globals in event handlers on Android Browser 4 one has to add window in front of ko eg. window.ko see screenshot below.
so fix was as simple as changing
var data = ko.dataFor(boundEl);
to
var data = window.ko.dataFor(boundEl);

'load' event not firing when iframe is loaded in Chrome

I am trying to display a 'mask' on my client while a file is dynamically generated server side. Seems like the recommend work around for this (since its not ajax) is to use an iframe and listen from the onload or done event to determine when the file has actually shipped to the client from the server.
here is my angular code:
var url = // url to my api
var e = angular.element("<iframe style='display:none' src=" + url + "></iframe>");
e.load(function() {
$scope.$apply(function() {
$scope.exporting = false; // this will remove the mask/spinner
});
});
angular.element('body').append(e);
This works great in Firefox but no luck in Chrome. I have also tried to use the onload function:
e.onload = function() { //unmask here }
But I did not have any luck there either.
Ideas?
Unfortunately it is not possible to use an iframe's onload event in Chrome if the content is an attachment. This answer may provide you with an idea of how you can work around it.
I hate this, but I couldn't find any other way than checking whether it is still loading or not except by checking at intervals.
var timer = setInterval(function () {
iframe = document.getElementById('iframedownload');
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
// Check if loading is complete
if (iframeDoc.readyState == 'complete' || iframeDoc.readyState == 'interactive') {
loadingOff();
clearInterval(timer);
return;
}
}, 4000);
You can do it in another way:
In the main document:
function iframeLoaded() {
$scope.$apply(function() {
$scope.exporting = false; // this will remove the mask/spinner
});
}
var url = // url to my api
var e = angular.element("<iframe style='display:none' src=" + url + "></iframe>");
angular.element('body').append(e);
In the iframe document (this is, inside the html of the page referenced by url)
window.onload = function() {
parent.iframeLoaded();
}
This will work if the main page, and the page inside the iframe are in the same domain.
Actually, you can access the parent through:
window.parent
parent
//and, if the parent is the top-level document, and not inside another frame
top
window.top
It's safer to use window.parent since the variables parent and top could be overwritten (usually not intended).
you have to consider 2 points:
1- first of all, if your url has different domain name, it is not possible to do this except when you have access to the other domain to add the Access-Control-Allow-Origin: * header, to fix this go to this link.
2- but if it has the same domain or you have added Access-Control-Allow-Origin: * to the headers of your domain, you can do what you want like this:
var url = // url to my api
var e = angular.element("<iframe style='display:none' src=" + url + "></iframe>");
angular.element(document.body).append(e);
e[0].contentWindow.onload = function() {
$scope.$apply(function() {
$scope.exporting = false; // this will remove the mask/spinner
});
};
I have done this in all kinds of browsers.
I had problems with the iframe taking too long to load. The iframe registered as loaded while the request wasn't handled. I came up with the following solution:
JS
Function:
function iframeReloaded(iframe, callback) {
let state = iframe.contentDocument.readyState;
let checkLoad = setInterval(() => {
if (state !== iframe.contentDocument.readyState) {
if (iframe.contentDocument.readyState === 'complete') {
clearInterval(checkLoad);
callback();
}
state = iframe.contentDocument.readyState;
}
}, 200)
}
Usage:
iframeReloaded(iframe[0], function () {
console.log('Reloaded');
})
JQuery
Function:
$.fn.iframeReloaded = function (callback) {
if (!this.is('iframe')) {
throw new Error('The element is not an iFrame, please provide the correct element');
}
let iframe = this[0];
let state = iframe.contentDocument.readyState;
let checkLoad = setInterval(() => {
if (state !== iframe.contentDocument.readyState) {
if (iframe.contentDocument.readyState === 'complete') {
clearInterval(checkLoad);
callback();
}
state = iframe.contentDocument.readyState;
}
}, 200)
}
Usage:
iframe.iframeReloaded(function () {
console.log('Reloaded');
})
I've just noticed that Chrome is not always firing the load event for the main page so this could have an effect on iframes too as they are basically treated the same way.
Use Dev Tools or the Performance api to check if the load event is being fired at all.
I just checked http://ee.co.uk/ and if you open the console and enter window.performance.timing you'll find the entries for domComplete, loadEventStart and loadEventEnd are 0 - at least at this current time:)
Looks like there is a problem with Chrome here - I've checked it on 2 PCs using the latest version 31.0.1650.63.
Update: checked ee again and load event fired but not on subsequent reloads so this is intermittent and may possibly be related to loading errors on their site. But the load event should fire whatever.
This problem has occurred on 5 or 6 sites for me now in the last day since I noticed my own site monitoring occasionally failed. Only just pinpointed the cause to this. I need some beauty sleep then I'll investigate further when I'm more awake.

using onbeforeunload event, url change on selecting stay on this page

Rewriting the question -
I am trying to make a page on which if user leave the page (either to other link/website or closing window/tab) I want to show the onbeforeunload handeler saying we have a great offer for you? and if user choose to leave the page it should do the normal propogation but if he choose to stay on the page I need him to redirect it to offer page redirection is important, no compromise. For testing lets redirect to google.com
I made a program as follows -
var stayonthis = true;
var a;
function load() {
window.onbeforeunload = function(e) {
if(stayonthis){
a = setTimeout('window.location.href="http://google.com";',100);
stayonthis = false;
return "Do you really want to leave now?";
}
else {
clearTimeout(a);
}
};
window.onunload = function(e) {
clearTimeout(a);
};
}
window.onload = load;
but the problem is that if he click on the link to yahoo.com and choose to leave the page he is not going to yahoo but to google instead :(
Help Me !! Thanks in Advance
here is the fiddle code
here how you can test because onbeforeunload does not work on iframe well
This solution works in all cases, using back browser button, setting new url in address bar or use links.
What i have found is that triggering onbeforeunload handler doesn't show the dialog attached to onbeforeunload handler.
In this case (when triggering is needed), use a confirm box to show the user message. This workaround is tested in chrome/firefox and IE (7 to 10)
http://jsfiddle.net/W3vUB/4/show
http://jsfiddle.net/W3vUB/4/
EDIT: set DEMO on codepen, apparently jsFiddle doesn't like this snippet(?!)
BTW, using bing.com due to google not allowing no more content being displayed inside iframe.
http://codepen.io/anon/pen/dYKKbZ
var a, b = false,
c = "http://bing.com";
function triggerEvent(el, type) {
if ((el[type] || false) && typeof el[type] == 'function') {
el[type](el);
}
}
$(function () {
$('a:not([href^=#])').on('click', function (e) {
e.preventDefault();
if (confirm("Do you really want to leave now?")) c = this.href;
triggerEvent(window, 'onbeforeunload');
});
});
window.onbeforeunload = function (e) {
if (b) return;
a = setTimeout(function () {
b = true;
window.location.href = c;
c = "http://bing.com";
console.log(c);
}, 500);
return "Do you really want to leave now?";
}
window.onunload = function () {
clearTimeout(a);
}
It's better to Check it local.
Check out the comments and try this: LIVE DEMO
var linkClick=false;
document.onclick = function(e)
{
linkClick = true;
var elemntTagName = e.target.tagName;
if(elemntTagName=='A')
{
e.target.getAttribute("href");
if(!confirm('Are your sure you want to leave?'))
{
window.location.href = "http://google.com";
console.log("http://google.com");
}
else
{
window.location.href = e.target.getAttribute("href");
console.log(e.target.getAttribute("href"));
}
return false;
}
}
function OnBeforeUnLoad ()
{
return "Are you sure?";
linkClick=false;
window.location.href = "http://google.com";
console.log("http://google.com");
}
And change your html code to this:
<body onbeforeunload="if(linkClick == false) {return OnBeforeUnLoad()}">
try it
</body>
After playing a while with this problem I did the following. It seems to work but it's not very reliable. The biggest issue is that the timed out function needs to bridge a large enough timespan for the browser to make a connection to the url in the link's href attribute.
jsfiddle to demonstrate. I used bing.com instead of google.com because of X-Frame-Options: SAMEORIGIN
var F = function(){}; // empty function
var offerUrl = 'http://bing.com';
var url;
var handler = function(e) {
timeout = setTimeout(function () {
console.log('location.assign');
location.assign(offerUrl);
/*
* This value makes or breaks it.
* You need enough time so the browser can make the connection to
* the clicked links href else it will still redirect to the offer url.
*/
}, 1400);
// important!
window.onbeforeunload = F;
console.info('handler');
return 'Do you wan\'t to leave now?';
};
window.onbeforeunload = handler;
Try the following, (adds a global function that checks the state all the time though).
var redirected=false;
$(window).bind('beforeunload', function(e){
if(redirected)
return;
var orgLoc=window.location.href;
$(window).bind('focus.unloadev',function(e){
if(redirected==true)
return;
$(window).unbind('focus.unloadev');
window.setTimeout(function(){
if(window.location.href!=orgLoc)
return;
console.log('redirect...');
window.location.replace('http://google.com');
},6000);
redirected=true;
});
console.log('before2');
return "okdoky2";
});
$(window).unload(function(e){console.log('unloading...');redirected=true;});
<script>
function endSession() {
// Browser or Broswer tab is closed
// Write code here
alert('Browser or Broswer tab closed');
}
</script>
<body onpagehide="endSession();">
I think you're confused about the progress of events, on before unload the page is still interacting, the return method is like a shortcut for return "confirm()", the return of the confirm however cannot be handled at all, so you can not really investigate the response of the user and decide upon it which way to go, the response is going to be immediately carried out as "yes" leave page, or "no" don't leave page...
Notice that you have already changed the source of the url to Google before you prompt user, this action, cannot be undone... unless maybe, you can setimeout to something like 5 seconds (but then if the user isn't quick enough it won't pick up his answer)
Edit: I've just made it a 5000 time lapse and it always goes to Yahoo! Never picks up the google change at all.

Chrome extension: callback function not getting called

I am developing a small extension( https://docs.google.com/leaf?id=0B5ZSnXcRXnSpMmM0NTFiNGEtMzEzZS00M2YzLWI4MzItMmVmNmM3OGE1MDRh&hl=en&authkey=CLzGpOMN ) that saves all tabs in a particular window, while closing that session.
In this, when I am trying to restore the session, I am not getting callback function getting called, though the new window is successfully opened.
The funny thing is, when in developer mode, using developer tools, the callback function gets called and restored all tabs.
Please help me.
Here is the code:
function restoreTabs( saveTabName )
{
var tabVals = window.localStorage.getItem(saveTabName);
if (tabVals == null)
return;
var callbackFunc = function (window, tabValList) {
//alert('created window');
for (var i = 0; i < tabValList.length; i++) {
var tab = eval('(' + tabValList[i] + ')');
var newTabObj = {
windowId: window.id,
index: tab.index,
url: tab.url,
selected: tab.selected,
pinned: tab.pinned
};
chrome.tabs.create(newTabObj);
}
};
var tabValList = tabVals.split('|');
chrome.windows.create(null, function (win) { callbackFunc(win, tabValList); });
}
Interesting problem. Popup is getting automatically closed when you create a new window (and as a result popup code execution is terminated), that's why it works in developer mode only because it forces the popup to stay open. You need to move restoreTabs() function to a background page, you can still easily call it from your popup:
linka.onclick = function () {
chrome.extension.getBackgroundPage().restoreTabs('saveTabs'+savetabName);
};

Categories

Resources