I have one HTML page with parent.js in it. Within that html, one iFrame will be created on runtime. This iframe needs the parent.js in its context.
If I am having one
function save() { ... }
in the parent.js, I can call that from iframe.js, like
parent.save();
But i need to call that in the iframe context, like
save();
So I have loaded the parent.js again in the iframe html. This makes parent.js to be loaded everytime I create a new iframe.
Is there anyway I could reuse the parent.js which is already loaded, into each iframe created?, like
loadParentJS("parent.js");
within iframe.js. This shouldn't give another request to application server.
Encapsulate your code in parent.js in a closure:
var loadParentJS = function(window) {
window.save = function() {
// code
};
window.other = function() {
// code
};
// rest of your code...
};
loadParentJS(window);
Then in your iframe, run this:
parent.loadParentJS(window);
In your parent.js file, preceed each function and variable with this. and then you should be able to take advantage of Javascript Closures.
Replace: function save() { ... }
With: this.save = function() { ... }
Replace: var aVariable = "value";
With: this.aVariable = "value";
Then in your iframe you need to set the scope of this to parent:
this = parent;
All of your calls to functions or variables in parent.js (in the global javascript or in the iframe javascript) will look like this:
this.save();
alert(this.aVariable);
Short answer, no you can't. the content of IFrame is handled as a separate web page.
Longer answer: are you sure you need to use IFrame? what is it for? can you avoid it?
using IFrame is not a bad thing, just maybe it doesn't fullfill your needs
You should read a bit about cross domain communication. This will give you an idea on how to communicate between your web page and the IFrame inside it
cross domain communication
good luck
I guess you can pass the document or window to the save function to manipulate the context.
For example, accept the document as a parameter:
function save(doc) { doc.getObjectById("myform").submit(); }
Then:
parent.save(document);
Or accept the window as the parameter:
function save(win) { alert(win.myvariable) }
And then:
parent.save(self);
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 have a functionality that I had running in the
window.addEventListener('load', function() {
var variable_name_1 = localStorage.getItem('var_1');
...
}
and I would like to move the functionality such that it only runs when the user clicks a button, in here:
function maketempuser() {
...
}
I can get the function to call when I want. But the function utilizes tons of variables from the load function. Is there a clean way to "globalize" these variables? Or must I find some way to add all these variables in the html:
<button ... onclick='maketempuser(variable_name_1, variable_name_2, ...);' >
NOTE: the javascript will run the same file, I just don't want it to keep re-running every time the user reloads the page since there is an ajax mysql insert that occurs because this page is one in a line of pages that enables a user to register.
To not pollute the global scope with a lot of variables (which can be overridden by other apps), I recommend you create an object with an app specific name, maybe something like this
var myAppVar = {};
window.addEventListener('load', function() {
myAppVar.var_1 = localStorage.getItem('var_1');
...
}
Just define them in global scope:
var variable_name_1;
window.addEventListener('load', function() {
variable_name_1 = localStorage.getItem('var_1');
...
}
This, however, is not a particularly healthy technique, since it's prone to name collisions. Best thing to do is have a custom object (cO, or with your initials, something unlikely to be used by anything else) and use it as a placeholder for all your custom vars:
var cS = {
var_1:null // or some default value...
};
window.addEventListener('load', function() {
cS.var_1 = localStorage.getItem('var_1');
...
}
Since localStorage is already global just retrieve the values you need in your handler from there.
function maketempuser() {
var variable_name_1 = localStorage.getItem('var_1');
}
No need to add anything extra to the global scope at all.
My node-webkit application consists of a control window and a presentation window.
The control window gathers data and eventually launches the presentation window via the window.open function.
The presentation window now has access to some information in the global variable.
Now I want to render a graphical representation of that information by creating SVG elements and so forth.
I already have a javascript function to do exactly that, but I need some way of starting that function from the control window.
I can't call it directly, since then the function has to access the other window's DOM.
I have tried using the eval function on the other window's object, but that crashes node-webkit with the message
[18719:0522/205047:ERROR:breakpad_linux.cc(1225)] crash dump file written to
/tmp/chromium-renderer-minidump-788bf8d0c68301d5.dmp
What would be the best way to do this?
Use setInterval to regularly check a global variable?
One solution I've found to be pretty effective is attaching pub/sub functionality to the globalvariable. The setup I've used so far is jQuery-based, though it could also be constructed without. First it is initialized using a variant of this code:
var messages = messages || {};
global.Message = global.Message || function( id ) {
var callbacks, method,
message = id && messages[ id ];
if ( !message ) {
callbacks = jQuery.Callbacks();
message = {
publish: callbacks.fire,
subscribe: callbacks.add,
unsubscribe: callbacks.remove
};
if ( id ) {
messages[ id ] = message;
}
}
return message;
};
Then anywhere between the windows events can be published and subscribed to using the following pattern. One window can publish the data:
global.Message("someButtonClicked").publish(data);
Then the other can listen for it.
global.Message("someButtonClicked").subscribe(onButtonClicked);
function onButtonClicked(data) {
console.log(data);
};
You can inject and eval a piece of JS by using Window.eval():
https://github.com/rogerwang/node-webkit/wiki/Window#windowevalframe-script
I have a WebBrowser control in my C# application. The web browser is under the user's control, that is, he can load any web page his computer can access on the web (of course limited by proxy, hosts file and so on).
I need to know and to be notified when there is a Javascript call inside the page loaded in the web browser component.
First example: given a link like this
test
When the user clicks the link I need to know that the function "jsFunct" has been called.
Second example: given a call like
<script type="text/javascript">
window.setTimeout("jsFunct()", 1000);
</script>
I need to know that, 1 second after the execution of the script, the function jsFunct has been called.
The best thing would be to have an event fired when the function is called. It would also be great if the event could get the Javascript code executed, or at least the function name in the arguments.
EDIT:
Even if the question is related to the webbrowser component, anything that allows the user to detect javascript activation (even via js) would be fine, being able to inject a js that handles the javascript event and passes it to the wb control triggering some event that it can handle.
You can use window.external to call a C# method when a global function is fired in JavaScript. See WebBrowser Control Overview for details on window.external.
You'll need to set ObjectForScripting: Webbrowser control's window.external is ALWAYS null. for this to work.
Take #Krishna's answer to add the JavaScript (but drop jQuery because it won't be needed):
private void addScript(HtmlElement head, string scriptSource)
{
HtmlElement lhe_script = head.Document.CreateElement("script");
IHTMLScriptElement script = (IHTMLScriptElement)lhe_script.DomElement;
script.src = scriptSource;
head.AppendChild(lhe_script);
}
addScript(WebBrowser.Head, #"InjectMonitor.js");
The JavaScript below (InjectMonitor.js) will find all global functions and attach your specified handler:
function augment(withFn) {
var name, fn;
for (name in window) {
fn = window[name];
if (typeof fn === 'function') {
window[name] = (function(name, fn) {
var args = arguments;
return function() {
withFn.apply(this, args);
fn.apply(this, arguments);
};
})(name, fn);
}
}
}
augment(function(name, fn) {
console.log("calling " + name, fn);
// window.external.yourC#method
});
In this example, taken from Adding Console Log to Every Function, it just logs the call to console; but using window.external you could send some message back to your C# application with details of what function was called from the client.
Finally, here's a JS Bin example (run it and don't forget the console): JS Bin Example
On the webbrowser load event,
Inject Jquery
Inject Monitor scripts
,
private void addScript(HtmlElement head, string scriptSource)
{
HtmlElement lhe_script = head.Document.CreateElement("script");
IHTMLScriptElement script = (IHTMLScriptElement)lhe_script.DomElement;
script.src = scriptSource;
head.AppendChild(lhe_script);
}
addScript(Webbrowser.Head, #"<Change File Path here>jquery.min.js");
addScript(WebBrowser.Head, #"InjectMonitor.js");
your file InjectMonitor.js should be something like this
$(document).ready(function () {
//Add click event for every anchor on the page loaded- note this merely alerts text on click. you can however add your own function
$("a").click(function (e) { alert($(this).text()); return false;})
});
Well what krishna has answered is interms of pure javascript attaching to events, however i see that you might need to attach it to all the tags(a,p,div,input) etc and to all the events attached to each tag.
i believe the another way is to play around with the BHO(browser helper object) available to your in .net, and if not and you are good at VC++ and MFC you can also play around with Windows Hooks.
Sorry I couldn't be anymore specific with the title.
I'm building a web-site (personal), which displays different content to the user depending on the query string that is used in the url.
e.g. page=home.html would display home.html
The websites Javascript is wrapped inside an object, with each value containing different data, some pseudo code:
(function(){
var wrapper = {
init: function(){
//Runs on document ready
this.foo();
this.nav.render();
},
foo: function(){
//Some functionality goes here for the website, e.g. Display something from an API
},
nav: {
//Functionality to handle the navigation, has different properties
config: {
//Contains the config for nav, e.g. page names + locations
dir: '/directory/to/content/',
pages: {
page_name: wrapper.nav.config.dir + 'page_value'
}
},
render: function(){
//some code
},
routes: function(){
//some code}
}
}
};
$(function(){
wrapper.init();
});
})();
My problem is that I'm trying to prepend the dir value to each of the page values (inside the object where the pages are defined), expecting to get the output of (in this pseudo code case) of directory/to/content/page_value, but instead dir is undefined when I'm trying to access it, I've tried the following to achieve what I want:
wrapper.nav.config.dir + 'page_value'
I've been playing around with the last 30 minutes trying to find out what I'm doing wrong, and even thought about hard-coding the URL in for each page.
The reasoning for wanting to do this is that my local development server and web host have different directory structures, so I don't want to re-write the URL's each time I want to develop + publish. As for why everything is wrapped inside an object, I thought it would be easier to maintain this way.
Hopefully the answer is simple and it's just an amateur mistake / lack of understanding.
The issue is that you can't refer to a variable that is being defined in that very definition.
So, inside the definition of wrapper, you can't refer to wrapper. And, inside the definition of config, you can't refer to config either and so on.
The usual design pattern for solving this is to initialize as much as you can in the declaration of your data structure and then do the rest in .init() when you can freely access all of it.
Change the first two lines to:
var wrapper = null;
(function(){
wrapper = {
Otherwise, the wrapper is a local variable to your anonymous function.
The problem is that you're still busy defining the wrapper when you ask for its value, which is why it's still undefined.
The code below fails too:
var x = {
y:"1",
z:x.y
}
Why not:
//...
init: function(){
//Runs on document ready
this.foo();
var config = this.nav.config;
for (var page in config.pages) {
config.pages[page] = config.dir + config.pages[page];
}
},
//...