I am trying to run a script in a new tab. The code I use is this:
$ = jQuery;
function openAndPush(url, id) {
var win = window.open('https://example.com' + url + '?view=map');
var element = $('<script type="text/javascript">console.log("Starting magic...");var region_id='+id+';$=jQuery;var p=$(\'div["se:map:paths"]\').attr(\'se:map:paths\');if(p){console.log("Found! pushing..."); $.get(\'https://localhost:9443/addPolygon\', {id: region_id, polygon: p}, function(){console.log("Done!")})}else{console.log("Not found!");}</script>').get(0);
setTimeout(function(){ win.document.body.appendChild(element);
console.log('New script appended!') }, 10000);
}
Considering the following:
I was inspired in this answer, but used jQuery instead.
I run this code in an inspector/console, from another page in https://example.com (yes, the actual domain is not example.com - but the target url is always in the same domain with respect to the original tab) to avoid CORS errors.
When I run the function (say, openAndPush('/target', 1)) and then inspect the code in another inspector, one for the new window, the console message Starting magic... is not shown (I wait the 10 seconds and perhaps more). However the new DOM element (this script I am creating) is shown in the Elements tab (and, in the first console/inspector, I can see the New script appended! message).
(In both cases jQuery is present, but not occupying the $ identifier, which seems to be undefined - so I manually occupy it)
What I conclude is that my script is not being executed in the new window.
What am I missing? How can I ensure the code is being executed?
Instead of embedding script element in the document, do this.
wrap the code that you want to run in another tab, into a function.
bind that wrapped function to the new tab's window
Call that function
Here's the code that I ran in my console and it worked for me i.e another tab was opened and alert was displayed.
function openAndPush(url, id) {
var win = window.open('https://www.google.com');
win.test = function () {
win.alert("Starting magic...");
}
win.test();
setTimeout(function () {
win.document.body.appendChild(element);
console.log('New script appended!')
}, 10000);
}
Found that my error consisted on the origin document being referenced when creating a new script node, instead of the target document (i.e. win.document). What I needed is to change the code to reference the new document and create the node directly, no jQuery in the middle at that point. So I changed my code like this:
function openAndPush(url, id) {
var win = window.open('https://streeteasy.com' + url + '?view=map');
var element = win.document.createElement('script');
element.type='text/javascript';
element.innerHTML = 'console.log("Starting magic...");var region_id='+id+';$=jQuery;var p=$(\'div[se\\\\:map\\\\:paths]\').attr(\'se:map:paths\');if(p){console.log("Found! pushing..."); $.get(\'https://localhost:9443/addPolygon\', {id: region_id, polygon: p}, function(){console.log("Done!")})}else{console.log("Not found! searched in: ", document);}'
setTimeout(function(){ win.document.body.appendChild(element); console.log('New script appended!') }, 10000);
}
With this code something is essentially happening: The JS code is being parsed (and its node created) in the context of the new document. Older alternatives involved the origin console (since the origin document was implicitly referenced).
It's bad practice to send scripts to another webpage. You can pass some query params using a complicated URL and handle it by a source code from another webpage, it's much better:
function doMagicAtUrlByRegionId (url, regionId) {
window.open(`https://example.com${url}?view=map&magic=true®ion_id=${regionId}`);
}
Related
Is it possible.. to have my javascript, example as in below; to simply continue to execute with my click function but the changes are only reflective in a new window - while current page (non-new window does not change via the JS) is this possible?
$('.download-pdf').click(function() {
$(this).attr('target', '_blank');
notChecked = $("input[type=checkbox]:not(:checked)").parent();
notChecked.hide();
yesChecked = $("input[type=checkbox]:checked").parent();
$.each(yesChecked, function( index, el ) {
$(el).show().html(texts[$(el).attr('id')]);
});
You can use the postMessage API which is perfectly described in this SO Answer. You can do this only, if the new window has the same origin.
Probably you'd need to wait some time for the new frame to be fully loaded.
I have an iframe that's supposed to load different modules of a web application.
When the user clicks a navigation menu in the top window, it's passes a new url to the iframe. The trouble is, the new url doesn't actually point to a new page, it only uses a changed hash.
i.e.:
User clicks "dashboard", iframe src set to application.html#/dashboard
User clicks "history", iframe src set to application.html#/history
This means that the iframe does not actually load the src url again because hash changes don't require it to. The application inside the iframe is an angular app which loads the required modules dynamically using requireJS. We need this functionality to remain.
I need to force the frame source to load again even though only the hash changed. It's possible that I instead find a way to rewrite our angular app to dynamically unload/load the modules on push state events but that introduces several layers of issues for the app, plus some IE trouble.
I've tried:
Setting iframe src and calling it's location.reload, but that reloads the originally loaded url
Setting the iframe location.href/hash and calling reload, same issue
Blanking the src attribute and then setting the new url - no effect
The only solution I can find is to set the src to a blank screen, then onload set it to the new url:
var appIFrame = document.getElementById('appIFrame');
appIFrame.src = 'about:blank';
appIFrame.onload = function(){
appIFrame.src = '// set the real source here';
appIFrame.onload = false;
}
This works, yet it seems inefficient because there's an extra step.
Maybe add a dynamic GET parameter – f.e. the current timestamp, which you can get from the JavaScript Date object – to the iframe URL.
Instead of assigning application.html#/dashboard as src value, assign application.html?1234567890#/dashboard from your outside page (with 1234567890 replaced by the current timestamp, obviously).
I don't have a specific answer for you. However, the following script may proved useful (I wrote this about a year or so ago). The following script deals with re-adjusting iframe height when the document changes. This script was tested cross-browser. It does deal with the issues you're experience but indirectly. There is a lot of commenting with the Gist:
https://gist.github.com/say2joe/4694780
Here my solution (based on this stackoverflow answer):
var $ = function(id) { return document.getElementById(id); };
var hashChangeDetector = function(frame, callback) {
var frameWindow = frame.contentWindow || frame.contentDocument;
// 'old' browser
if (! "onhashchange" in window) {
var detecter = function(callback) {
var previousHash = frameWindow.location.hash;
window.setTimeout(function() {
if (frameWindow.location.hash != previousHash) {
previousHash = frameWindow.location.hash;
callback(previousHash);
}
}, 100);
};
}
else // modern browser ?
{
var detecter = function(callback) {
frameWindow.onhashchange = function () {
callback(frameWindow.location.hash);
}
};
}
detecter(callback);
};
hashChangeDetector($('myframe'), function(hash) {
alert ('detecting hash change: ' + hash);
});
You can test this here: http://paulrad.com/stackoverflow/iframe-hash-detection.html
I have been doing some research on opening a new window and writting HTML to it with jQuery/JavaScript and it seems like the proper way to do it is to:
Create a variable for the new window
var w = window.open();
Insert the new data and work the variable
$(w.document.body).html(data);
And to me, that makes complete sense. however when i try to incorporate this into my script ("data" being the holder for the HTML) it does not open a new window... unless I'm just missing something simple which as far as I can tell it looks great...
function newmore(str) {
var identifier = 4;
//get the history
$.post("ajaxQuery.php", {
identifier : identifier,
vanid : str
},
//ajax query
function(data) {
//response is here
var w = window.open();
$(w.document.body).html(data);
});//end ajax
}
Any ideas?
P.S. there seems to be no errors in the console
Your new window is probably being blocked by the popup blocker built into most browsers. If you create the new window as a direct result of a user action (key, click), then the browser usually will not block it. But, if you wait until sometime later (like after an ajax call completes), then it will get blocked and that is probably what is happening in your case.
So, the fix is usually to create the window immediately in direct response to the user event (don't wait until the ajax call completes), keep the window handle in a variable and then put the content in the window later after your ajax call completes.
function newmore(str){
var identifier = 4;
// create window immediately so it won't be blocked by popup blocker
var w = window.open();
//get the history
$.post("ajaxQuery.php", {
identifier : identifier,
vanid : str
},
//ajax query
function(data) {
//response is here
$(w.document.body).html(data);
});//end ajax
}
Try instead:
var w = window.open();
w.document.write(data);
The "innerHTML" property of the document object (which is what jQuery's .html() uses) represents the HTML document, which a new window doesn't have. Even if it did, putting a complete document inside an HTML document doesn't really make sense. It's a freshly-created document, so you can just write to it.
This peace of code will work:
var data = "<h1>Test</h1>";
var w = window.open("", "mywindow1", "width=350,height=150");
$(w.document.body).html(data);
You have to inform some parameters when opening new windows.
But, if possible, I'd hardly recommend that you use another way like, jquery UI or Twitter Bootstrap for doing that, so you will not be using pop-ups.
I'm developing a web application that opens a popup using windows.open(..). I need to call a function on the opened window using the handle returned by "window.open", but I'm always getting the error message "addWindow.getMaskElements is not a function", as if it couldn't access the function declared on child window. This is the behavior in both IE and FF. My code looks like this:
function AddEmail(target,category)
{
if(addWindow == null)
{
currentCategory = category;
var left = getDialogPos(400,220)[0];
var top = getDialogPos(400,220)[1];
addWindow = window.open("adicionar_email.htm",null,"height=220px, width=400px, status=no, resizable=no");
addWindow.moveTo(left,top);
addWindow.getMaskElements ();
}
}
I've googled and read from different reliable sources and apparently this is supposed to work, however it doesn't.
One more thing, the functions in child window are declared in a separate .js file that is included in the adicionar_email.htm file. Does this make a difference? It shouldn't..
So, if anyone has ran into a similar problem, or has any idea of what I'm doing wrong, please, reply to this message.
Thanks in advance.
Kenia
The window creation is not a blocking operation; the script continues to execute while that window is opening and loading the HTML & javascript and parsing it.
If you were to add a link on your original page like this:
Test
You'd see it works. (I tried it just to be sure.)
**EDIT **
Someone else posted a workaround by calling an onload in the target document, here's another approach:
function AddEmail()
{
if(addWindow == null) {
addWindow = window.open("test2.html",null,"height=220px, width=400px, status=no, resizable=no");
}
if(!addWindow.myRemoteFunction) {
setTimeout(AddEmail,1000);
} else { addWindow.myRemoteFunction(); }
}
This keeps trying to call addWindow.myRemoteFunction every 1 second til it manages to sucessfully call it.
The problem is that window.open returns fairly quickly, the document that is requested and then any other items that that document may subsequently refer to will not yet have been loaded into the window.
Hence attempting to call this method so early will fail. You should attach a function to the opened window's load event and attempt to make you calls from that function.
The problem with the below one is :
When the javascript is being executed in the parent window, the child window is not loading. Hence, the invoking function from parent window is in the infinite loop and it is leading to crashing the window.
The window creation is not a blocking operation; the script continues
to execute while that window is opening and loading the HTML &
javascript and parsing it.
If you were to add a link on your original page like this:
Test
You'd see it works. (I tried it just to be sure.)
**EDIT **
Someone else posted a workaround by calling an onload in the target
document, here's another approach:
function AddEmail()
{
if(addWindow == null) {
addWindow = window.open("test2.html",null,"height=220px, width=400px, status=no, resizable=no");
}
if(!addWindow.myRemoteFunction) {
setTimeout(AddEmail,1000);
} else { addWindow.myRemoteFunction(); }
}
This keeps trying to call addWindow.myRemoteFunction every 1 second
til it manages to sucessfully call it.
You are calling the function immediately after opening the window; the page on the popup may not be loaded yet, so the function may not be defined at that point.
I need to send a value to an iframe.
The iframe is present within the current window. How can I achieve this?
I need to do it with javascript in the parent window that contains the iframe.
First, you need to understand that you have two documents: The frame and the container (which contains the frame).
The main obstacle with manipulating the frame from the container is that the frame loads asynchronously. You can't simply access it any time, you must know when it has finished loading. So you need a trick. The usual solution is to use window.parent in the frame to get "up" (into the document which contains the iframe tag).
Now you can call any method in the container document. This method can manipulate the frame (for example call some JavaScript in the frame with the parameters you need). To know when to call the method, you have two options:
Call it from body.onload of the frame.
Put a script element as the last thing into the HTML content of the frame where you call the method of the container (left as an exercise for the reader).
So the frame looks like this:
<script>
function init() { window.parent.setUpFrame(); return true; }
function yourMethod(arg) { ... }
</script>
<body onload="init();">...</body>
And the container like this:
<script>
function setUpFrame() {
var frame = window.frames['frame-id'].contentWindow;
frame.yourMethod('hello');
}
</script>
<body><iframe name="frame-id" src="..."></iframe></body>
Depends on your specific situation, but if the iframe can be deployed after the rest of the page's loading, you can simply use a query string, a la:
<iframe src="some_page.html?somedata=5&more=bacon"></iframe>
And then somewhere in some_page.html:
<script>
var params = location.href.split('?')[1].split('&');
data = {};
for (x in params)
{
data[params[x].split('=')[0]] = params[x].split('=')[1];
}
</script>
Here's another solution, usable if the frames are from different domains.
var frame = /*the iframe DOM object*/;
frame.contentWindow.postMessage({call:'sendValue', value: /*value*/}, /*frame domain url or '*'*/);
And in the frame itself:
window.addEventListener('message', function(event) {
var origin = event.origin || event.originalEvent.origin; // For Chrome, the origin property is in the event.originalEvent object.
if (origin !== /*the container's domain url*/)
return;
if (typeof event.data == 'object' && event.data.call=='sendValue') {
// Do something with event.data.value;
}
}, false);
Don't know which browsers support this, though.
Use the frames collection.
From the link:
var frames = window.frames; // or // var frames = window.parent.frames;
for (var i = 0; i < frames.length; i++) {
// do something with each subframe as frames[i]
frames[i].document.body.style.background = "red";
}
If the iframe has a name you may also do the following:
window.frames['ponies'].number_of_ponies = 7;
You can only do this if the two pages are served from the same domain.
Two more options, which are not the most elegant but probably easier to understand and implement, especially in case the data that the iframe needs from its parent is just a few vars, not complex objects:
Using the URL Fragment Identifier (#)
In the container:
<iframe name="frame-id" src="http://url_to_iframe#dataToFrame"></iframe>
In the iFrame:
<script>
var dataFromDocument = location.hash.replace(/#/, "");
alert(dataFromDocument); //alerts "dataToFrame"
</script>
Use the iFrame's name
(I don't like this solution - it's abusing the name attribute, but it's an option so I'm mentioning it for the record)
In the container:
<iframe name="dataToFrame" src="http://url_to_iframe"></iframe>
In the iFrame:
<script type="text/javascript">
alert(window.name); // alerts "dataToFrame"
</script>
We can use "postMessage" concept for sending data to an underlying iframe from main window.
you can checkout more about postMessage using this link
add the below code inside main window page
// main window code
window.frames['myFrame'].contentWindow.postMessage("Hello World!");
we will pass "Hello World!" message to an iframe contentWindow with iframe id="myFrame".
now add the below code inside iframe source code
// iframe document code
function receive(event) {
console.log("Received Message : " + event.data);
}
window.addEventListener('message', receive);
in iframe webpage we will attach an event listener to receive event and in 'receive' callback we will print the data to console
Incase you're using angular and an iframe inside, you'll need need to listen to the iframe to finish loading. You can do something like this:
document.getElementsByTagName("iframe")[0].addEventListener('load', () => {
document.getElementsByTagName("iframe")[0].contentWindow.postMessage(
{
call: 'sendValue',
value: 'data'
},
window.location.origin)
})
You will have to get the iframe one way or another (there are better ways to do it in angular) and then wait for it to load. Or else the listener won't be attached to it even if you do it inside lifecycle methods like ngAfterViewInit()
Have a look at the link below, which suggests it is possible to alter the contents of an iFrame within your page with Javascript, although you are most likely to run into a few cross browser issues. If you can do this you can use the javascript in your page to add hidden dom elements to the iFrame containing your values, which the iFrame can read.
Accessing the document inside an iFrame
Just another way.
From iframe you can add event listeners and dispatch events into parent document:
parent.document.addEventListener('iframe-event', (e) => {
console.log('iframe-event', e.detail);
});
setTimeout(() => {
console.log('dispatchEvent from iframe');
const event = new CustomEvent('frame-ready', { detail: 'parent event dispatched from iframe' });
parent.document.dispatchEvent(event);
}, 1000);
And from parent you can add event listeners and dispatch events in its own document:
document.addEventListener('frame-ready', (e) => {
const event = new CustomEvent('iframe-event', { detail: 'iframe event dispatched from parent' });
document.dispatchEvent(event);
});
Also if in any case you need to open this frame in a new tab you can still use events for communication.
From your frame/tab:
if (opener) {
const event = new CustomEvent('frame-ready', { detail: 'parent event dispatched from new tab' });
opener.document.dispatchEvent(event);
}
From your parent/opener:
window.open('my-frame.html', '_blank');
document.addEventListener('frame-ready', (e) => {
const event = new CustomEvent('iframe-event', { detail: 'iframe event dispatched from parent' });
document.dispatchEvent(event);
});
Just be aware that window.open will expose your DOM to next page it opens, so if you use it to open any third part url, you must always use rel=noopener to avoid security issues:
window.open('third-part-url.html', '_blank', 'noopener');
In your main homepage, add this line-
window.postMessage("Hello data from Homepage");
Inside your iframe , add this line-
window.addEventListener("message", receiveDataFromWeb);
const receiveDataFromWeb= (data)=> {
console.log(data);
//this should print Hello data from Homepage
}
What you have to do is to append the values as parameters in the iframe src (URL).
E.g. <iframe src="some_page.php?somedata=5&more=bacon"></iframe>
And then in some_page.php file you use php $_GET['somedata'] to retrieve it from the iframe URL. NB: Iframes run as a separate browser window in your file.