I have an aspx page that has a reference to index.js inside the js I have a closures like thes one below, then from the page I open a popup window which has a reference to page1.js , Is there any way to call the closure and assign it to a variable in page.js, so I can use the closure methods and update the _var1.
Im opening the popup window within the same domain as the parent index.aspx page. I already tried using code in page.js like: var _closure = window.opener.UI_Methods(); but I got the following error: Unable to evaluate the expression. Call was rejected by callee. in the Immediate Window on VS 2010 when debugging.
Is there a way I can call a closure in the parent page and update variables by calling the closure methods?
UI_Methods = function(){
var _var1 = var1 || null;
var _setVar1 = function(var1){
_var1 = var1;
};
var _getVar1 = function(){
return _var1;
};
return {
SetVar1 : _serVar1
};
};
Related
I need to include a reference to JavaScript written by a third party on my website. Sadly, the developers that wrote this script decided to define all of their functions globally. You know, like this:
function AwesomeStringHelper() {
// ...
}
function MyGreatFunction() {
// ...
}
When I reference this script using a <script> tag, both of those methods will be added to the window object.
Since I prefer to not pollute the global scope, is there a way that I can change the scope of an external script? Ideally I'd like to be able to refer to these methods similar to ExternalLibrary.MyGreatFunction(), etc. I am not able to modify the third party script as it is hosted externally, and it changes frequently.
In the first instance, try to edumacate the third party developers on how to correctly write their modules.
If that doesn't work, do:
var ExternalLibrary = ExternalLibrary || window;
at the top of your code.
You can then use ExternalLibrary.MyGreatFunction() throughout to refer to their functions (even though they remain visible in the global window scope), and then later once the third party devs have fixed their scope issues then at most you need a one line change to maintain compatibility (or no change at all, if they happen to use the same ExternalLibrary name as you do).
Alternatively, use two simple snippets of code either side of the <script> tag which remember the keys of the window object, then move the newly appeared keys into a new object (at the same time deleting them from window):
Pre-load:
var ExternalLibrary = { _current: Object.keys(window) };
Post-load:
Object.keys(window).forEach(function(key) {
if (ExternalLibrary._current.indexOf(key) < 0) {
ExternalLibrary[key] = window[key];
delete window[key];
}
});
delete ExternalLibrary._current;
I've used a similar approach in the past (before strict mode was common) to check for leaking global variables.
If your third-party module assigns to the window object directly (like window.myGlobal = someValue), and you are able to download the source code manually, you should be able to "wrap" the entire script in a function, where the window object has been overloaded:
function wrapModule(code) {
// create a "fake" window object that inherits from the global object
var fakeWindow = Object.create(window);
// create a function wrapping the code
// note that "window" is a parameter name in this function, shadowing
// the global object
var func = Function("window", code);
// call function
func.call(fakeWindow, fakeWindow);
// return fake window object
return fakeWindow;
}
// run code
const fakeWindow = wrapModule(`
var x = 0; // local variable (will not be exported)
y = 1; // global variable (will still be leaked)
window.z = 2; // assignment to window
this.w = 3; // assignment to this
`);
// check what variables are exposed
console.log('window.x', typeof x); // window.x undefined
console.log('window.y', typeof y); // window.y number
console.log('window.z', typeof z); // window.z undefined
console.log('window.w', typeof w); // window.w undefined
// check what variables are exposed in fakeWindow
console.log('fakeWindow.x', typeof fakeWindow.x); // fakeWindow.x undefined
console.log('fakeWindow.y', typeof fakeWindow.y); // fakeWindow.y number
console.log('fakeWindow.z', typeof fakeWindow.z); // fakeWindow.z number
console.log('fakeWindow.w', typeof fakeWindow.w); // fakeWindow.w number
Assuming you know the specific functions being defined, then after the script is loaded, would this not work?
const ThirdPartyLib = {AwesomeStringHelper, MyGreatFunction};
delete window.AwesomeStringHelper;
delete window.MyGreatFunction;
ThirdPartyLib.AwesomeStringHelper(haveFun);
You can wrap the entire script in a function and return an object with the "public" functions you want, it can be tedious and hard to maintain.
var myLib = function() {
//entire script
return {
functionA : functionA,
functionB : functionB,
//rest of functions
}
}
Or like this (inmediately invoked function)
(function(global) {
//entire script
myLib.functionA = functionA;
myLib.functionB = functionB;
//rest of fn
global.myLib = myLib;
})(window);
You could automate this using gulp, i'm not sure if there's a good plugin for this.
Not sure if jQuery is an option or if you care for it but I don't know how to write native JS AJAX calls so bear with me:
$(document).ready(function(){
$.ajax({
url: 'www.example.com/awesome_script.js', // get the contents of the external script
type: 'GET',
crossDomain: true,
dataType: 'html',
success: function(data){
// build our script tag and wrap the contents inside of a function call
var script = "<script>"
script+= "var callMe = function(call_func, var1, var2, var3){";
script+= data;
script+= "return typeof call_func === 'function' ? call_func(var1, var2, var3) : 'You trying to dynamically call a variable? idk how to do that.';";
script+= "};";
script+= "<\/script>";
// assuming this is legal then just append the custom script tag to the <body> :-)
$('body').append($(script)[0]);
// profit?
callMe('AwesomeStringHelper', 'some_var'); // this function accepts one parameter
callMe('MyGreatFunction'); // this function accepts no parameters
}
});
});
My previous colleague rewrite window.print method:
function print(data){
var window_print = window.open('', 'my div', 'height=768, width=1024');
window_print.document.write('<!DOCTYPE html><html><head><title>Печать</title></head><body>' + data + '</body></html>');
window_print.print();
window_print.close();
}
My intention was to use default behavior of that function: just print current page, and I added:
if(data) {....} else { window.print() }
And of course I received error: "too much recursion: window.print();"
My question is how to invoke default behavior window.print()?
Edit: Ok, it appears that print is an own property of window in some browsers, and isn't in others. Therefore, just cache the value of window.print:
var printWindow = window.print;
// define your new print function here
var print = function(data) { ... };
// then later:
printWindow.call(window);
NB: If you're doing all this in the global scope, then you'll need to define the new print using a function expression (var print = ...) rather than a function declaration (function print(data) { ... }) because function declarations get hoisted to the top of their scope, and therefore the print redefinition would happen before you had chance to cache it. If you're not doing it in the global scope, then it doesn't matter as the new print won't override window.print regardless of how it's defined.
Original:
Try:
Object.getPrototypeOf(window).print.call(window);
print doesn't appear to be an own property of window, meaning that the newly defined print merely shadows something further up the prototype chain. You can bypass this shadowing by moving up the prototype chain using Object.getPrototypeOf.
You'll also need to use call so that the print method receives the correct value for this.
You need to store the original print method as another property in the window, just before the definition of your own print().
EDIT: You also need to define the new print function specifically as window.print = function(){...} rather than function print(){...} in order to be able to access the original - see answers with nice links here and here. This won't have any impact on how you call the method.
window.originalPrint = window.print;
window.print = function(data)
{
if(data)
{
document.getElementById('foo').innerHTML = data;
}
else
{
window.originalPrint();
}
}
<div id="foo"></div>
<button onclick="window.print('hello')">print('hello')</button>
<button onclick="window.print()">print()</button>
This is my main file:
$(document).ready(function () {
function Page() {
this.menu = new Menu();
this.management = new Management();
this.text = "text";
}
window.Page= Page();
});
Now I want to access the Page from every other JS file:
I tried this:
console.log(Page.text);
Gives me : Uncaught ReferenceError: Page is not defined
Tried this:
console.log(window.Page.text);
Gives me : Uncaught TypeError: Cannot read property 'text' of undefined
What am I doing wrong?
Your issue is that within the Page function you are not creating any new object on the global context. You are creating a new Menu and new Management instance but on the current context.
Plus by calling the Window.Page = Page(), you are assigning the result of the Page function (which is void) to the window.Page object.
I suggest you do something like :
//- from any js file
function createPage() {
var newPage = { title : 'new page', count : '2' };
return newPage;
}
window.Page = createPage();
...
//- and from other js file
$(document).ready(function () {
alert(window.Page.title);
});
Note, I have replaced your menu and management properties with dummy content for this sample. Sample available # this JSFiddle
Update : Code has been updated to illustrate the multiple js file proper usage.
Hope this helps
Function definitions don't need to be inside the document.ready() function. Only immediate actions that need to take place when the DOM is ready need to be put in there. So move the function to the toplevel.
You need to use window.Page = new Page();.
Either
window.Page = new Page();
or
function Page(){
this.menu = new Menu();
this.management = new Management();
this.text = "text";
return this;
}
window.Page = Page();
Then make sure other scripts don't try to use window.Page before it has been declared. You declare it in document.ready() callback function, so it's only going to be accessible once the DOM is ready and the callback function has been fired.
Edit:
without the context I'm not sure this is exactly what you're trying to do, but I think you just need a global Page object with some properties/methods. The easiest way to create it would be
window.Page = {
menu : new Menu(),
management = new Management(),
text = "text"
};
without the document.ready() wrapper.
Now obviously Menu and Management need to be defined before this code is executed. If any of these functions relies on DOM just move all your scripts to the end of the document. Any script that needs access to window.Page has to be included after this one.
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);
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!