I'm not using eval, and I'm not sure what the problem is that Crockford has with the following. Is there a better approach to solve the following problem or is this just something I need to ignore (I prefer to perfect/improve my solutions if there is areas for improvement).
I'm using some pixel tracking stuff and in this case a client has bound a JS function to the onclick property of an HTML image tag which redirects off the site. I need to track the clicks reliably without running into race conditions with multiples of event listeners on the image. The strategy is to override the event at run time, copying and running it in my own function. Note this is being applied to a site I do not control and cannot change. So the solution looks something like:
...
func = Function(img.attr('onclick'));
...
img.attr('onclick', '');
... //some custom tracking code
func.call(this);
and the JSLint checker throws the eval is evil error.
Is there a better way to avoid race conditions for multiple events around href actions?
You're implicitly using eval because you're asking for the callback function as it was specified as an attribute in the HTML as a string and then constructing a Function with it.
Just use the img.onclick property instead, and you will directly obtain the function that the browser built from the attribute that you can then .call:
var func = img.onclick; // access already compiled function
img.onclick = null; // property change updates the attribute too
... // some custom tracking code
func.call(img, ev); // call the original function
or better yet:
(function(el) {
var old = el.onclick;
el.onclick = function() {
// do my stuff
..
// invoke the old handler with the same parameters
old.apply(this, arguments);
}
})(img);
The advantage of this latter method are two fold:
it creates no new global variables - everything is hidden inside the anonymous closure
It ensures that the original handler is called with the exact same parameters as are supplied to your replacement function
var oldClick = myImg.onclick;
myImg.onclick = function(evt){
// Put you own code here
return oldClick.call( this, evt );
};
Related
I Have this JavaScript code:
var cr = {};
cr.plugins_ = {};
cr.runtime = null;
cr.plugins_.Vinoos_Markets = function(runtime) {
this.runtime = runtime;
};
(function() {
function initialize_events(result) {
alert(result);
}
})();
<button onclick="initialize_events('Test Result');">Send Result</button>
how to run 'initialize_events' function from html by clicking on button?
I don't have access to editing JavaScript file.
i dont have access to editing js file.
Then you can't, full stop. It's entirely private to the anonymous IIFE* that encloses it. You'd have to expose it as a global in order to use it with an onxyz-attribute-style event handler (and that would require modifying the JavaScript code). It's one of the many reasons not to use them.
Since you can't do it without modifying the JavaScript, I'm going to assume you overcome that limitation and suggest what to do when/if you can modify the JavaScript:
Have that IIFE hook up the button, and use a data-* attribute if you need button-specific information to pass it:
var cr = {};
cr.plugins_ = {};
cr.runtime = null;
cr.plugins_.Vinoos_Markets = function(runtime) {
this.runtime = runtime;
};
(function() {
function initialize_events(result) {
alert(result);
}
document.getElementById("send-result").addEventListener("click", function() {
initialize_events(this.getAttribute("data-result"));
}, false);
}());
<button id="send-result" data-result="Test Result">Send Result</button>
Notes:
If you need to support obsolete browsers without addEventListener (such as IE8, which is sadly still a requirement for many), see this answer for a cross-browser event hooking function.
If you have the data in the IIFE rather than the button, you can just use it directly rather than using a data-* attribute.
Giving the button an ID and using getElementById is just an example; in practice, anything that lets you identify the button is all you need. You can look up using a full CSS selector via document.querySelector.
* IIFE = immediately-invoked function expression, e.g., (function() { /*...*/})(); (Also sometimes called an "inline-invoked function expression." Also sometimes erroneously called a "self-invoking function," but it isn't; it's invoked by the code defining it, not by the function itself.)
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.
As of Firefox 36, Function.__exposedProps__ was made unavailable. Instead if one wanted to expose a chrome JS object to be used in content scripts, they have to use Components.utils.cloneInto with the target scope as browser.contentWindow.wrappedJSObject.
If one does not turn on the cloneFunctions flag, only those attributes are cloned that are not functions. Turning the flag does clone functions too, but not those functions that are defined via the Function.prototype path. For those functions one has to export them via Components.utils.exportTo with the target scope as your exposed object.
Coming to the issue I'm facing. (As I am unable to put it in words, I am adding a MWE).
Chrome end JS:
function Foo(){
this._nFunc = "something";
this._func = function(){/*do something*/};
}
Foo.prototype.Bar = function(){
this._func();
}
Foo.prototype.FooBar = function(){
this._nFunc = "somthing else";
}
var myFoo = new Foo();
var targetScope = browser.contentWindow.wrappedJSObject;
targetScope.myExposedObject = Components.utils.cloneInto(myFoo, targetScope, {cloneFunctions:true});
Components.utils.exportFunction(myFoo.Bar, targetScope.myExposedObject , {defineAs:"Bar"});
Components.utils.exportFunction(myFoo.FooBar, targetScope.myExposedObject , {defineAs:"FooBar"});
Content end JS:
window.myExposedObject.FooBar(); // works
window.myExposedObject._func(); // works
window.myExposedObject.Bar() // error this._func is undefined
Upon logging the this scope received by the function Bar(), we get _func:(void 0), while _nFunc is logged correctly.
Questions:
Is there something I'm missing, or is this a limitation in Firefox? If it is a limitation, please suggest possible ways to workaround the limitation.
Initially I thought that Bar() was somehow unable to access the scope of the calling object, and I tried to supply it the scope as parameters, i.e., Foo.prototype.Bar = function(scope){ scope._func();} and window.myExposedObject.Bar(window.myExposedObject);. Interestingly upon logging, the scope object also turned out to be (void 0). Why is that? I am sure that I am missing something here. What I expected was that the exposed object would map to the original object and upon sending the exposed object as parameters the chrome end JS would be able to get the original object.
While what you're trying to do might be possible with the right combination of cloneInto/exportFunction and waiving of xrays i would suggest you simply load the unprivileged part of your class hierarchy directly into the target context with the subscript loader and only hook the minimal amount of privileged functions into the prototype once it has been created.
This should reduce the attack surface and also avoid headaches with inheritance.
Additionally, these may prove useful:
https://developer.mozilla.org/en-US/docs/Components.utils.createObjectIn
https://developer.mozilla.org/en-US/docs/Components.utils.makeObjectPropsNormal
As we all know when we create a class in javascript a normal function returns the class object but events return the event object and the class object gets lost
function class(a){
this.name=a;
document.addEventListener('click',this.click,false);
xhr.addEventListener('load',this.xhr,false);
this.normal()
}
class.prototype={
click:function(e){
//e=event,this=theDocument //can't access class
},
xhr:function(e){
//e=event,this=theXHR //can't access class
},
normal:function(e){
//e=null,this=class
}
}
Whats the best way to bind those events to our class?
by best way i mean no or just a tiny reference, the ability to remove the events in a native way(removeEventListener) and to absolutely not create memory leaks.
1.to remove an eventlistener you need to pass the function as a reference, so addEventListener('click',function(){alert('something')},false) does not work.
2.i read references like var that=this inside functions create leaks, true?.
Known ways
function class(a){
this.name=a;
var that=this;// reference
//simply reference the whole object as a variable.
var bindedClick=this.click.bind(this);//bind the click to the class
//(js 1.85)
//can i use removeEventListener('click',bindedClick,false) later?
//apply && call (js 1.3)
}
As i'm not shure if var that=this (as it is the whole object) creates leaks, sometimes i minimize this by saving the info into an array and as reference i use a id..
var class={};
var id='ID'+Date.now();
class[id].info={here i store all the info i need later text only}
//this will be stored also in a cookie / Localstorage to reuse later.
class[id].dom={here i store the dom references}
class[id].events{here i store the xhr events}//if needed
//this are Temp only
and to get the info i just pass the id by adding it to the event element
class[id].events.xhr.id=id;
class[id].events.xhr.onload=class.f
class.prototype.f=function(e){
//this.response,class[this.id]<- access everything.
this.removeEventListener('load',class.f,false);
delete class[this.id].events.xhr;
delete this.id
}
class[id].dom.id=id;
class[id].dom.onclick=class.f2
class.prototype.f2=function(e){
//class[e.target.id],class[this.id]<- access everything.
this.removeEventListener('click',class.f2,false);
delete class[this.id].dom;
delete this.id
}
As you can see in this example above i have acess to everything and the reference is just a small string...
I store the dom because i define the dom references on load so i don't have to call getElementById() everytime;
I store the events like XHR in the class as i want to be able to call xhr.abort() from outside.. and also able to call removeEventListener.
I need to minimize the impact to the memory but at the other side i need to control many elements that have multiple simultaneous events to control the garbage collector "manually" by removing all events and references.
To make sure you understand that the problem is bigger thn it looks...:
it's a download manager for chrome. input field for download url:
1.xhr to get the fileinfo (size,name,acceptranges,mime)
2.store info in localstorage and cached array
2.create visual dom elements to show progress (event:progress,click,mouseover..)
3.xhr request a chunk 0-1000(event:load)
4.onprogress display progress data (event:progress,error)
5.request filesystem(event:ok,error)
6.readfile/check file (event:ok,error)
7.request filewriter (event:ok,error)
8.append to file (events=ok,error)
9.on ok start again from 3. until file is finished
10.when finished i delete all the **references / events / extra info**
//this to help the garbage collector.
11.change the dom contents.
Every file has sooo many events every sec.
Which of these 3 is the best solution or are there any better solutions?
bind();//or call / apply
var that=this; //reference to the whole object
var id=uniqueid; // reference to the object's id
BASED ON the answers:
(function(W){
var D,dls=[];
function init(){
D=W.document;
dls.push(new dl('url1'));
dls.push(new dl('url2'));
}
function dl(a){
this.MyUrl=a;
var that=this;
var btn=D.createElement('button');
btn.addEventListener('click',this.clc,false);
D.body.appendChild(btn);
}
dl.prototype={
clc:function(e){
console.log(that)
}
}
W.addEventListener('load',init,false);
})(window)
var that=this does not work.
this works... but i need alot of checks , swich if and execute multiple functions.
(function(W){
var D,dls=[];
function init(){
D=W.document;
dls.push(new dl('url1'));
dls.push(new dl('url2'));
}
function dl(a){
this.MyUrl=a;
this.btn=D.createElement('button');
btn.addEventListener('click',this,false);
D.body.appendChild(btn);
}
dl.prototype={
handleEvent:function(e){
e.target.removeEventListener('click',this,false);//does this the work?
return this.clc(e);
},
clc:function(e){
console.log(this,e)
}
}
W.addEventListener('load',init,false);
})(window)
BIND :
(function(W){
var D,dls=[];
function init(){
D=W.document;
dls.push(new dl('url1'));
dls.push(new dl('url2'));
}
function dl(a){
this.MyUrl=a;
this.clcB=this.clc.bind(this);
this.btn=D.createElement('button');
this.btn.addEventListener('click',this.clcB,false);
D.body.appendChild(this.btn);
}
dl.prototype={
clc:function(e){
e.target.removeEventListener('click',this.clcB,false);//does this the work?
delete this.clcB;
console.log(this)
}
}
W.addEventListener('load',init,false);
})(window)
Better solution is to have your "class" implement the EventListener interface.
You do this by adding a handleEvent method to MyClass.prototype. This allows you to pass the object directly to .addEventListener() instead of passing a handler.
When an event occurs, the handleEvent() method will be invoked, with your object as the this value. This allows you to have access to all the properties/methods of the object.
function MyClass(a) {
this.name = a;
// pass the object instead of a function
document.addEventListener('click', this, false);
xhr.addEventListener('load', this, false); // where did `xhr` come from?
this.normal()
}
MyClass.prototype = {
// Implement the interface
handleEvent: function(e) {
// `this` is your object
// verify that there's a handler for the event type, and invoke it
return this[e.type] && this[e.type](e);
},
click: function (e) {
// `this` is your object
},
load: function (e) {
// `this` is your object
},
normal: function (e) {
// `this` is your object
}
}
Notice that I changed the name of your xhr method to load. This makes it easier to call the proper method based on the event type.
Then when it comes time to call .removeEventListener(), just do it like normal from the element, but again pass the object instead of the handler.
I read references like var that=this inside functions create leaks
Wrong. They create references that are not garbage-collected until the function is, but that's exactly what you want. It's not a leak.
It might cause problems in very old browsers (IE6) that cannot handle cyclic references, but simply don't care about those. Also, by calling removeEventListener you even destroy that cyclic reference so everything is fine.
I minimize this by saving the info into an array and the reference is just a small string...
No. The reference is your class array which will much more likely create a leak if you forget to delete the ids from id. Don't overcomplicate this.
Which of these 3 is the best solution or are there any better solutions?
var that=this; //reference to the whole object
The standard approach. Very fine.
.bind();
Can be more concise than a that variable, and has the same reference layout (no difference in garbage collection). Notice that a native bind is not available on old browsers, so some people frown upon this method. Fine as well, but might need a shim.
var id=uniqueid; // reference to the object's id
Don't do that. It's too complicated, and you easily make mistakes that really lead to huge leaks.
The event listener interface with a handleEvent method (presented by #CrazyTrain)
Very elegant, but unknown to the most people. Low memory footprint since no bound, privileged functions need to be created. Works very well for classes that need to handle only one event type, but needs some kind of delegation when supporting different events or different targets with the same listener instance (and can become more complex than the other approaches). Contra: The handlerEvent method is public and everything that has access to your instance can "fire" (spoof) events.
var that=this does not work.
You're using it wrong. The point of this approach is to create a closure in which new functions have access to the that variable. The closure scope is your constructor, you cannot access it from the prototype.
var that=this;
btn.addEventListener('click',function(e){that.clc(e);},false);
// use `this` in the prototype
handleEvent works... but i need alot of checks , swich if and execute multiple functions.
No. Your instances do only handle the click of the button, so this approach is fine for you. You even could put all of your code from clc directly into handleEvent.
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!