Javascript Object inexplicably bound to Window and Object - javascript

I've javascript object
var Bucket = function(id) {
this.id = id
this.items = []
}
Bucket.prototype.add_item = function(item) {
//some uniquness checking code here
this.items.push(item);
}
For some reason, when I instantiate the object and try to add an item, I get an error
var bct = new Bucket
bct.add_item(some_item)
'undefined' is not an object (evaluating 'this.items.push')
This error occurs even though the item is getting added to the array of the bucket
console.log(bct.items)
>> [some_item]
I used console.log(this) inside the add_items method and I see two entries being logged from the same line
>Object myjavascript.js:28
>DOMWindow myjavascript.js:28
There are some other libraries included on the page
Google Analytics
jQuery
Colorbox plugin for jQuery
jQuery Easing
Any ideas what is binding this object to Window?
FOUND IT (Edit)
somewhere else on the page, I was using a jquery ajax call in combination with my object... and it was binding it... sorry for not posting all of the relevant code...

You say "the item is getting added", but that is irrelevant, because according to your logging you have two calls to the method, one of which is correct. You need to focus on the incorrect one.
Make sure you have a debugger in your browser (e.g. Chrome with the Web Inspector opened), and modify your code like so:
Bucket.prototype.add_item = function(item) {
if (this === window) debugger;
//some uniquness checking code here
this.items.push(item);
}
This will automatically pause your code when the erroneous call has occurred. Then, look at the call stack to find out what is calling your add_item function other than as a method.

Related

Cross-origin object when everything is coming from localhost

I'm trying to record every time a user changes a text input on my web page. I'm using the following:
function formInit(socketObject) {
$('input:text').change( (eventObject) => {
console.log(document.domain);
console.log(JSON.stringify($(this)));
//... send stuff via socketObject
});
}
which I'm calling from $(document).ready(). I'm getting the following error on that fourth line, the one that logs $(this):
SecurityError: Permission denied to access property "toJSON" on cross-origin object
despite the fact that console.log(document.domain); returns localhost from both the script and from the master document it's being called from. The only thing that isn't being served up from my flask server on localhost are a couple of libraries (jquery, socketio, cleave) and possibly some components in a bokeh chart, which I'm pretty sure don't qualify as input:text, and certainly haven't been the thing I've been .change()ing in my debugging.
Can anyone suggest where I'm going wrong here, or where I should be looking? Thanks.
The error is because this is not the input:text element as you seem to be expecting, because you're using an arrow function, so lexical scope is not maintained. You either need to change the arrow function to an anonymous function:
function formInit(socketObject) {
$('input:text').change(function() {
// 'this' now refers to the element which raised the event
});
}
Alternatively keep the arrow function, but get a reference to the element from the event which is raised and provided as an argument to the handler function:
function formInit(socketObject) {
$('input:text').change(e => {
// 'e.target' now refers to the element which raised the event
});
}
Aside from the error, stringifying an entire jQuery object is a little odd. It's a better idea to just pull out only the relevant properties you need, something like this:
function formInit(socketObject) {
$('input:text').change((e) => {
console.log(document.domain);
console.log({
id: e.target.id,
value: e.target.value
// other properies here...
});
// send stuff via socketObject...
});
}

How to restore console.log on a web page that overwrites it?

Twitter’s website does something like
console.log = function () {};
to turn the browsers’ built-in console.log method into a no-op. Is there I way to restore the original function?
Unless they also removed it in the prototype, getting the log method using getPrototypeOf() should work:
console.log = Object.getPrototypeOf(console).log;
Since using console.log = function() {} overrides, but doesn't remove the one defined in the prototype, you can delete the overriding method:
delete console.log

Deferred timing issue

I have the following code I've designed to load and run script at runtime. You'll note that I save it to localStorage if it isn't already there. Now it runs fine if it's stored there already, but when it's just got the text from the file it throws ReferenceError: loginLaunch is not defined, though the text seems to have been loaded (hence the console.log lines that check the length). For your convenience I've included a line, localStorage.clear();, to make it alternate between the error message that's the problem and ReferenceError: loginLaunch is not defined, which given the code below is the desired result.
I don't understand why it should work one way and not the other. If it's a timing issue I don't see how the use of the promise, loginCode, lets it through unless possibly appendChild() is asynchronous, but I'm under the impression that it isn't (mainly because it has no callback, and I tried to find out, but could not) and even then why would code before the appendChild() have an impact?
Have I messed up one of the promises? I include the contents of the file login.js at the end. I searched SO for anything relevant but without any luck except for just one post that states that appendChild is synchronous.
Please help.
var loginCode = runCode("login_1001","./js/login.js");
loginCode.done(loginLaunch());
//FUNCTIONS START HERE
function getCode(local, source) { //This creates the promise to get the code (not to run it)
console.log("start of loadCode");
dfd = $.Deferred(); //This is the one to return.
script = localStorage.getItem(local); //Try to load from local storage.
// console.log("script after local attempt: "+script);
if (script) { //If found...
console.log("found Local code");
dfd.resolve(script);
localStorage.clear(); //Added for debugging
} else { //load from file.
ajax = $.ajax({
url : source,
cache : false,
dataType : "text", //load as text initially so that we can store it locally.
});
ajax.done(function(fromFile){
localStorage.setItem(local, fromFile); //store it locally.
//console.log("script after ajax attempt: "+script);
dfd.resolve(fromFile);
});
ajax.fail(function(){
dfd.reject("Error retrieving code. You may be disconnected");
});
}
return dfd.promise();
}
function runCode(local, source) {
dfd = $.Deferred(); //This is the one to return.
code = getCode(local, source); //local promise
code.done(function(retrievedCode){
console.log(retrievedCode.length);
var head = document.getElementsByTagName('head')[0]; //first head section
var el = document.createElement("script"); //named the same as the local storage
//script.type= 'text/javascript'; Redundant — it's the default
// el.id = local; //Probably redundant, but if we want to manipulate it later...
el.text = retrievedCode;
head.appendChild(el); //This shouldn't run anything, just make global functions that will be called later.
console.log(el.text.length);
dfd.resolve(); //If we need to return the node to manipulate it later we'd make the variable above and 'return' it here
});
return dfd.promise();
}
Here's the contents of the login.js file.
function loginLaunch(){
dfd = $.Deferred(); //This is the one to return.
loadElement("login.1001", "#content", "login.html");
//After the element has been loaded we have a disconnect — i.e. there's no promise waiting, so we have to wait for the user.
}
$("#content").delegate('#loginButton','click',function(){
console.log("Login click");
//php to pick up the entered details and pass them to common php that also uses the
checkCredentials = $.ajax({
type : "POST",
url : "./php/credentials.php",
data : {
queryString : queryString
},
datatype : "text", // 1 or 0
});
checkCredentials.done(credentialsChecked(success));
// MOVE THIS STUFF
readyPublicList();
$.when(publicListCode,loggedIn).then(runDefaultPublicList()); //Assumes successful login so it loads the code for the list window in parallel.
//Note that it's probable that my approach to the login window may change, because it needs to be available on the fly too.
// $("#content").html("<p>test</p>"); //Successfully tested, well it was once.
});
function loginHide(){
$("#loginHtml").hide;
}
I'm not sure why this works:
var loginCode = runCode("login_1001","./js/login.js");
loginCode.done(function(){loginLaunch();});
and this doesn't:
var loginCode = runCode("login_1001","./js/login.js");
loginCode.done(loginLaunch);
My one thought is that maybe if you pass literal named functions to .done then they are validated when loginCode is created, while anonymous functions aren't validated until they are about to be run.
I should note that the error was appearing before the console.log output.
Maybe someone with a better grasp of the technicalities can clarify. For now I'm just happy to stop tearing my hair out, but I like to know how things work...
You need to change at least three things. First change this:
loginCode.done(loginLaunch());
to this:
loginCode.done(function() {loginLaunch()});
You need to be passing a function reference to the .done() handler so it can be called later. The way you had it, you were calling it immediately BEFORE loginCode() was done with its work, thus it was getting called too early.
In addition, loginLaunch doesn't exist yet so you can't pass a reference directly to it. Instead, you can pass a reference to a wrapper function that then calls loginLaunch() only after it finally exists.
And second, you need to declare your local variables with var so they aren't implicit globals and stomp on each other. For example, you have multiple functions who call each other trying to use the same global dfd. That is a recipe for disaster. Put var in front of it to make it a local variable so it's unique to that scope.
And third, el.text doesn't look like the right property to me for your script. Perhaps you meant to use .textContent or since you have jQuery, you can do:
$(el).text(retrievedCode);
In a couple style-related issue, ALL local variables should be declared with var before them so they are not implicit globals. This will bite you hard by causing mysterious, hard to track down bugs, even more so with async code.
And, you can generally use the promise returned by jQuery from ajax functions rather than creating your own.
To incorporate those improvements:
runCode("login_1001","./js/login.js").done(loginLaunch);
function getCode(local, source) { //This creates the promise to get the code (not to run it)
var script = localStorage.getItem(local); //Try to load from local storage.
if (script) { //If found...
localStorage.clear(); //Added for debugging
// return a resolved promise (since there's no async here)
return $.Deferred().resolve(script);
} else { //load from file.
// return the ajax promise
return $.ajax({
url : source,
cache : false,
dataType : "text", //load as text initially so that we can store it locally.
}).then(function(fromFile){
localStorage.setItem(local, fromFile); //store it locally.
return fromFile;
});
}
}
function runCode(local, source) {
return getCode(local, source).then(function(retrievedCode){
console.log(retrievedCode.length);
var head = document.getElementsByTagName('head')[0]; //first head section
var el = document.createElement("script"); //named the same as the local storage
$(el).text(retrievedCode);
head.appendChild(el); //This shouldn't run anything, just make global functions that will be called later.
console.log(el.text.length);
});
}
FYI, if you just want to insert a script file, you don't have to manually retrieve the script with ajax yourself. You can use the src property on a script tag and let the browser do the loading for you. You can see a couple ways to do that here and here.

Changing the current closure?

I am trying to get some old code to work properly with minimal modification. The code was written on the assumption that it would run from a particular context. I have the context object.
Original code:
function oldExample(){
console.log(window); //New Error: window is undefined
console.log(gBrowser); //New Error: gBrowser is undefined
}
New, working code:
function fixedExample(){
console.log(this.window);
console.log(this.gBrowser);
}
//Elsewhere
function loadData(context) {
fixedExample.call(context);
}
Notes:
1. loadData and oldExample are defined in separate files.
2. context has other children besides window and gBrowser; This is an example
Is there a way to transition my oldExample code to work properly without needing to stuff this. everywhere? I.e., how can I run oldExample in a different context?
The only way I know how to do this is to define the properties as variables of the current context:
var object = {gBrowser: 'test'};
function oldExample(){
console.log(gBrowser);
}
var gBrowser = object.gBrowser;
oldExample();
This example outputs 'test'
But all this does is move the property access outside of the function definition, doesn't save you anything.
You can use bind method in javascript.
fixedExample.bind (context);
Now you need not use 'this' inside fixedExample and can use window directly.

Creating a JavaScript callback in an iframe

UPDATE: The back-end service was powered by an ASP.Net AJAX Web Service proxy.
1) The main page has two global objects, one for the back end connections (Svc) and another for handling the DOM (Main). It also dynamically loads documents into an iframe.
2) These iframes need to access services provided by Svc, and also supply a callback function.
3) The problem - passing a function created in the iframe to the parent frame, it's treated as an object not a function and cannot be invoked.
Can anyone provide a better solution than what I've got currently in the iframe:
var Main = parent.Main,
Svc = parent.Svc;
Svc.method(data, Main.createCallback(
function(response) {}
));
and in the parent frame:
Main.createCallback = function(func) {
return function() {
func.apply(func, arguments);
}
}
if you override the iFrame's function from the main, the main scope will then be used.
The inverse problem can be seen here, in your case, you just override the frame's function itself i.e:
document.getElementById('yourFrameID').contentWindow.targetFunctionInFrame = targetFunctionInMain;
Bonus: if you can modify the iFrame's code, I would suggest to:
In the frame:
make a placeholder function callbackParent() {}
add a call to this function into your iframe code, so that you just have to override the callbackParent from your main.
In the main:
make the function which should be invoked function doStuff() {}
override the function as described above document.getElementById('yourFrameID').contentWindow.callBackParent = doStuff;
I use iframes to modularize my app too.They are a kind of includes embedding all CSS, HTML and JS for a module.
My first attempts were by returning a function too, but then I found it quite hard for sharing scopes.
Now I make directly a reference to the main parent object in the iframe.
eg:
var Svc = parent.Svc, JSON = parent.JSON, $ = parent.$;
Svc.module1 = {
method1:function(arg){
...
},
...
}
The global var JSON and jQuery references are here to have them available inside the methods.
My guest is that Svc.method is making some checks to see if the callback has some criteria before calling it. This criteria might be that the callback function must created by the same framework (here it's ASP.Net). You have to find what that criteria is. if "Main.createCallback" works, it's because it's meeting that criteria.
Sorry but your all wrong... add this....
const event = new CustomEvent('MSGSent', { detail: "fff variable" });
Call it like this....use a global variable for detail... like an array []
window.dispatchEvent(event);
Now after the iframe loads add this code and you get an Object back in the main page....
iframe.onload = function() {
try {
iframe.contentWindow.addEventListener('MSGSent',function(e){
alert(e.detail);
});
} catch (error) {
}
};
The problem is ASP.Net AJAX Web Service proxies, which don't appear to support calling the web service from an iframe with an inline callback function.

Categories

Resources