This question asks for a way to open a new window using window.open and then inject it with a script. It was not possible because of cross-domain security issues.
However, my problem is that I want to do the exact same thing, except from the same domain to the same domain. Is this possible?
Note that .write does not solve this problem because it wipes all the html from the page first.
You can do something like this:
var theWindow = window.open('http://stackoverflow.com'),
theDoc = theWindow.document,
theScript = document.createElement('script');
function injectThis() {
// The code you want to inject goes here
alert(document.body.innerHTML);
}
theScript.innerHTML = 'window.onload = ' + injectThis.toString() + ';';
theDoc.body.appendChild(theScript);
This also seems to work:
var theWindow = window.open('http://stackoverflow.com'),
theScript = document.createElement('script');
function injectThis() {
// The code you want to inject goes here
alert(document.body.innerHTML);
}
// Self executing function
theScript.innerHTML = '(' + injectThis.toString() + '());';
theWindow.onload = function () {
// Append the script to the new window's body.
// Only seems to work with `this`
this.document.body.appendChild(theScript);
};
And if for some reason you want to use eval:
var theWindow = window.open('http://stackoverflow.com'),
theScript;
function injectThis() {
// The code you want to inject goes here
alert(document.body.innerHTML);
}
// Self executing function
theScript = '(' + injectThis.toString() + '());';
theWindow.onload = function () {
this.eval(theScript);
};
What this does (Explanation for the first bit of code. All examples are quite similar):
Opens the new window
Gets a reference to the new window's document
Creates a script element
Places all the code you want to 'inject' into a function
Changes the script's innerHTML to load said function when the window
loads, with the window.onload event (you can also use addEventListener). I used toString() for convenience, so you don't have to concatenate a bunch of strings. toString basically returns the whole injectThis function as a string.
Appends the script to the new window's document.body, it won't actually append it to the document that is loaded, it appends it before it loads (to an empty body), and that's why you have to use window.onload, so that your script can manipulate the new document.
It's probably a good idea to use window.addEventListener('load', injectThis.toString()); instead of window.onload, in case you already have a script within your new page that uses the window.onload event (it'd overwrite the injection script).
Note that you can do anything inside of the injectThis function: append DIVs, do DOM queries, add even more scripts, etc...
Also note that you can manipulate the new window's DOM inside of the theWindow.onload event, using this.
Yes...
var w = window.open(<your local url>);
w.document.write('<html><head>...</head><body>...</body></html>');
Here's a trick I use, it uses query strings, and is client side. Not perfect but it works:
On the sending page, do:
var javascriptToSend = encodeURIComponent("alert('Hi!');");
window.open('mypage.html?javascript=' + javascriptToSend);
Replace mypage.html with your page. Now on the receiving page, add:
(location.href.match(/(?:javascript)=([^&]+)/)[1])&&eval(decodeURIComponent(location.href.match(/(?:javascript)=([^&]+)/)[1]));
You'll have to do some back-and forth to make sure this works.
If you HAVE PHP you can use this more reliable solution on the receiving page:
eval(decodeURIComponent(<?=$_GET['javascript'] ?>));
Related
I'd like to run some javascript in the context of an iframe's window. Right now the only way I can think to do that is to inject a script tag:
myIframe = document.createElement('iframe');
myIframe.setAttribute('name', 'xyz123');
document.body.appendChild(myIframe);
myIframe.contentWindow.document.write(`
<script>
console.log('The current window name is:', window.name);
</script>
`);
Note: this is a same-domain iframe, without a src, so I have full access to the contentWindow.
It's important for my use case that the code runs with the correct globals; window, document etc should all be scoped to the iframe itself.
Is there any other way I can do this? The above works, but the script needs to run on different domains all with different CSP rules, which means adding support for nonces/hashes etc.
Is it possible to do something like:
myIframe.contentWindow.run(function() {
console.log('The current window name is:' window.name);
});
I've tried myIframe.contentWindow.setTimeout but that still seems to run the code in the context of the parent window.
You can actually create that run function, and then apply a callback function to this which of course will be the iframe context. Then you can access iframe elements by using this:
myIframe.contentWindow.run = function(fn) {
fn.apply(this);
};
myIframe.contentWindow.run(function() {
console.log('(run) The current window name is:', this.window.name);
});
Console output
(run) The current window name is: xyz123
You can check my example here: http://zikro.gr/dbg/html/con-frame/
EDIT
If you want to just use window rather than this.window, then you can create a parameter to the inline function with he name window, and then just pass this.window to that function like this:
myIframe.contentWindow.run = function(fn) {
fn.call(this, this.window);
};
myIframe.contentWindow.run(function(window) {
console.log('(run) The current window name is:', window.name);
});
And it still works as expected.
Maybe split the javascript to part run from main window (let's call it main.js) and from iframe (let's call it iframe.js). Then in iframe's src place link to iframe.js or iframe.html which loads js file (I'm not sure if you can include javascript straight from src attribute).
If you load js into the iframe, use solution at Calling a function inside an iframe from outside the iframe.
window.name='main window'; // just for debugging
var myIframe = document.createElement('iframe'), frameScript = document.createElement('script');
document.body.appendChild(myIframe);
frameScript.textContent = "window.top.name='topWindow';window.name = 'xyz123';function WhereAmI(){return(window.name);} window.parent.postMessage('frame_initialized', '*'); "
myIframe.contentWindow.document.documentElement.appendChild(frameScript);
function OnMessage(event) {
if(typeof event.data == 'string') switch(event.data) {
case 'frame_initialized':
myIframe.contentWindow.document.body.appendChild( document.createTextNode(myIframe.contentWindow.WhereAmI()) );
console.log('Now running in', myIframe.contentWindow.WhereAmI());
break;
}
}
window.addEventListener('message', OnMessage, false);
Tested with Firefox and Chromium.
Instead of .textContent you can apply the .src to frameScript, so the script can load asynchronously. Then you can call postMessage as shown above or call a callback function to notify the parent window.
Note that in your original code window.frameElement.name is initialized. Your script asks then for window.name.
FireFox copies the value automatically to the window, causing some confusion.
You need to load the script asynchronously (i.e. $.getScript()) and then invoke it on .contentWindow. I haven't tested but this should work.
(function() {
var myIframe = document.createElement('iframe#iframe'):
var jsSnippet;
var iframeScript = function(url) {
script = document.createElement('script'),
scripts = document.getElementsByTagName('script')[0];
script.src = url;
return jsSnippet = scripts.parentNode.insertBefore(script, scripts);
};
myIframe.setAttribute('name', 'xyz123'):
myIframe.appendChild(jsSnippet);
document.body.appendChild(myIframe);
return document.getElementById('#iframe').contentWindow.iframeScript();
})
These two resources will be helpful if this solution doesn't work:
https://plainjs.com/javascript/ajax/load-a-script-file-asynchronously-49/
Invoking JavaScript code in an iframe from the parent page
I am making a bookmarklet and need to change some code inside a page. For example, after page loaded it creates a function which is used 'onclick'. I need to replace a code inside a variable of this function. For example here is a function:
function openNewWindow(){
newWindow = window.open('http://www.example.org','params','width=200,height=200,resizable=0');
And I need to change this code into this:
function openNewWindow(){
newWindow = window.open('http://www.example.org','params','_blank');
How can I do it, taking in account, that the function is loaded by ajax?
Functions can be overwritting by being asigned a new refrence, if you only have access to the front end of the code after the fact. You can replace openNewWindow with a new function;
openNewWindow = function () {
newWindow = window.open('http://www.example.org','params','_blank');
}
However replacing functions that come from third parties are not recomended in a lot of cases because it can produce unexpected results.
I am in the middle of creating a small script to 'help' me with my homework. It uses jQuery. The script (so far) is below:
var s = document.createElement('script');
document.body.appendChild(s);
s.src = "http://code.jquery.com/jquery-latest.js"; // Include jQuery
var tmp_1 = document.embeds[0].GetVariable("q1answers"); // Read raw values
var answers_1 = tmp_1.split(","); // Explode into array
answers_1.splice(0,1); // Remove first element (always 0)
var tmp_2 = document.embeds[0].GetVariable("q2answers");
var answers_2 = tmp_2.split(",");
answers_2.splice(0,1);
answers_1.push("LINE_BREAK");
var answers = answers_1.concat(answers_2);
$("body").append("<div id='answers-wrap'></div>");
$("#answers-wrap").css("position", "fixed");
$("#answers-wrap").css("background", "none");
The problem arises when it gets to the 3rd-to-last line. Chrome console claims that Object #<HTMLBodyElement> has no method 'append', however if I extract that line and put it into the console on its own, it works fine. I can use a different method to insert HTML, but I would like to know what isn't working with this one.
Thanks in advance!
Since you're adding the jQuery script dynamically, it's loaded asynchronously, so it's probably not loaded yet when you're trying to use it. Use an onload handler for the dynamic script block:
var s = document.createElement('script');
document.body.appendChild(s);
s.src = "http://code.jquery.com/jquery-latest.js"; // Include jQuery
s.onload = function() {
$("body").append("<div id='answers-wrap'></div>");
$("#answers-wrap").css("position", "fixed");
$("#answers-wrap").css("background", "none");
}
The error message you're getting also indicates that $ exists (another library, maybe?) and is not returning a jQuery object, so you'll probably have to use jQuery in "noConflit" mode, and use jQuery or a user-defined alias instead of $.
Just a guess, but may you are running the script before the browser has finished rendering the DOM?
Try wrapping the code in
window.onload = function(){
// ... your code here
};
in order to execute it onload.
EDIT: changed code to reflect the feedback below, of course one cannot use jQuery's $ before jQuery is loaded, my fault.
I have a pretty specific question. I am trying to implement an onclick and cross domain tracking within a block of text, but it looks like it may need to be put directly into a .js document. I don't have a lot of JS experience. Basically, the current code looks like:
// JavaScript Document
function popup_no_status(loc)
{
var windowW=1000
var windowH=700
s = "width="+windowW+",height="+windowH+",status=yes, resizable=yes, scrollbars=yes";
mywin = window.open(loc ,'CBE', s);
mywin.focus();
}
What I want to add to this is:
onclick="pageTracker._trackEvent('Button', 'Click', 'QuickSearchWidget'); pageTracker._link(this.href); return false;
Can I just add it to the end of the document before the closing bracket? Any Ideas?
Much appreciated!
As long as the object pageTracker is defined and instantiated, you can call its methods like any other function:
function popup_no_status(loc) {
var s = "width=700,height=1000,status=yes, resizable=yes, scrollbars=yes";
var mywin = window.open(loc ,'CBE', s);
mywin.focus();
pageTracker._trackEvent('Button', 'Click', 'QuickSearchWidget');
pageTracker._link(this.href);
}
Also, the variables windowW and windowH are pointless in your example code - there is no need to store the string values in a variable if all you're going to do is concatenate them into another string. Further, unless you intend the mywin and s variables to be global, you should use the var keyword before defining them - that restricts the variables to the function scope instead of the global scope (all variables declared in a function without the var keyword are considered global).
If the code above gives an error like ReferenceError: pageTracker is not defined, that means that the code in which the pageTracker object is defined is either not included on the page, or it has not been instantiated.
Now... as for onClick, I am not clear what you're after here. Do you want this function to run when someone clicks the document? That would get pretty annoying!
When I use code like this, it works fine:
function removeWarning() {
var systemStatus = document.getElementById("system-status");
systemStatus.innerHTML = "";
}
function indicateInvalidUsername() {
var systemStatus = document.getElementById("system-status");
systemStatus.innerHTML = "Invalid username";
}
However, when I then want to move the systemStatus to be a global variable, it doesn't work:
var systemStatus = document.getElementById("system-status");
function removeWarning() {
systemStatus.innerHTML = "";
}
function indicateInvalidUsername() {
systemStatus.innerHTML = "Invalid username";
}
What am I supposed to be doing here?
It really depends on where your JavaScript code is located.
The problem is probably caused by the DOM not being loaded when the line
var systemStatus = document.getElementById("system-status");
is executed. You could try calling this in an onload event, or ideally use a DOM ready type event from a JavaScript framework.
Make sure you declare the variable on "root" level, outside any code blocks.
You could also remove the var altogether, although that is not recommended and will throw a "strict" warning.
According to the documentation at MDC, you can set global variables using window.variablename.
My guess is that the system-status element is declared after the variable declaration is run. Thus, at the time the variable is declared, it is actually being set to null?
You should declare it only, then assign its value from an onLoad handler instead, because then you will be sure that it has properly initialized (loaded) the element in question.
You could also try putting the script at the bottom of the page (or at least somewhere after the system-status element is declared) but it's not guaranteed to always work.
Declare systemStatus in an outer scope and assign it in an onload handler.
systemStatus = null;
function onloadHandler(evt) {
systemStatus = document.getElementById("....");
}
Or if you don't want the onload handler, put your script tag at the bottom of your HTML.
A global variable would be best expressed in an external JavaScript file:
var system_status;
Make sure that this has not been used anywhere else. Then to access the variable on your page, just reference it as such. Say, for example, you wanted to fill in the results on a textbox,
document.getElementById("textbox1").value = system_status;
To ensure that the object exists, use the document ready feature of jQuery.
Example:
$(function() {
$("#textbox1")[0].value = system_status;
});
To define a global variable which is based off a DOM element a few things must be checked. First, if the code is in the <head> section, then the DOM will not loaded on execution. In this case, an event handler must be placed in order to set the variable after the DOM has been loaded, like this:
var systemStatus;
window.onload = function(){ systemStatus = document.getElementById("system_status"); };
However, if this script is inline in the page as the DOM loads, then it can be done as long as the DOM element in question has loaded above where the script is located. This is because javascript executes synchronously. This would be valid:
<div id="system_status"></div>
<script type="text/javascript">
var systemStatus = document.getElementById("system_status");
</script>
As a result of the latter example, most pages which run scripts in the body save them until the very end of the document. This will allow the page to load, and then the javascript to execute which in most cases causes a visually faster rendering of the DOM.