JavaScript eval() with `this` - javascript

If I define a JavaScript code snippet in my HTML, like so:
<div id=myElem onMyUpdate="alert('Update called for ' + this.id)">...
then what is the most elegant way of evaluating it from within JavaScript with this properly assigned?
What I came up with so far is something like this:
if (elem.hasAttribute('onMyUpdate'))
(function () { eval(elem.getAttribute('onMyUpdate')) }).call(elem);
which looks terrible (to me), but works. Any better/more elegant alternatives?
MDN says there used to be the second argument to eval() for doing just that but it's deprecated now; MDN then suggests to use operator with() instead, which, if you follow the link provided, turns out to be made deprecated by the latest standard. Dead end, in other words.
(As a side note, StackOverflow ignores the word this in search terms and thus it may miss relevant answers - is there a way of telling it not to?)
Edit: I forgot to mention: no jQuery please, just vanilla JavaScript

How about this:
if(elem.hasAttribute('onMyUpdate')) {
var fun = new Function(elem.getAttribute('onMyUpdate'));
fun.call(elem);
}

Ideally, you should do this completely unobtrusively, and without the use of eval:
<div id="myElem"></div>
.
var elem = document.getElementById('myElem');
elem.onMyUpdate = function () {
alert(this.id);
};
// ...
elem.onMyUpdate && elem.onMyUpdate();

Instead of using this you can use elem.
<div id=myElem onMyUpdate="alert('Update called for ' + elem.id)">...
js:
if (elem.hasAttribute('onMyUpdate')){ // all variable available here will be available inside eval.
eval( elem.getAttribute('onMyUpdate') );
}

A more elegant solution would be to use custom events, bind handlers to some custom events on your HTML elements and trigger them in some other parts of your code. See this tutorial and the answer by Sidharth Mudgal for some examples.

Related

Why "$().ready(handler)" is not recommended?

From the jQuery API docs site for ready
All three of the following syntaxes are equivalent:
$(document).ready(handler)
$().ready(handler) (this is not recommended)
$(handler)
After doing homework - reading and playing with the source code, I have no idea why
$().ready(handler)
is not recommended. The first and third ways, are exactly the same, the third option calls the ready function on a cached jQuery object with document:
rootjQuery = jQuery(document);
...
...
// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
return rootjQuery.ready( selector );
}
But the ready function has no interaction with the selector of the selected node elements, The ready source code:
ready: function( fn ) {
// Attach the listeners
jQuery.bindReady();
// Add the callback
readyList.add( fn );
return this;
},
As you can see, it justs add the callback to an internal queue( readyList) and doesn't change or use the elements in the set. This lets you call the ready function on every jQuery object.
Like:
regular selector: $('a').ready(handler) DEMO
Nonsense selector: $('fdhjhjkdafdsjkjriohfjdnfj').ready(handler) DEMO
Undefined selector:$().ready(handler) DEMO
Finally... to my question: Why $().ready(handler) is not recommended?
I got an official answer from one of the jQuery developers:
$().ready(fn) only works because $() used to be a shortcut to $(document) (jQuery <1.4)
So $().ready(fn) was a readable code.
But people used to do things like $().mouseover() and all sorts of other madness.
and people had to do $([]) to get an empty jQuery object
So in 1.4 we changed it so $() gives an empty jQuery and we just made $().ready(fn) work so as not to break a lot of code
$().ready(fn) is literally now just patched in core to make it work properly for the legacy case.
The best place for the ready function is $.ready(fn), but it's a really old design decision and that is what we have now.
I asked him:
Do you think that $(fn) is more readable than $().ready(fn) ?!
His answer was:
I always do $(document).ready(fn) in actual apps and typically there's only one doc ready block in the app it's not exactly like a maintenance thing.
I think $(fn) is pretty unreadable too, it's just A Thing That You Have To Know Works™...
Since the different options do pretty much the same thing as you point out, it's time to put on the library writer hat and make some guesses.
Perhaps the jQuery people would like to have $() available for future use (doubtful since $().ready is documented to work, even if not recommended; it would also pollute the semantics of $ if special-cased).
A much more practical reason: the second version is the only one that does not end up wrapping document, so it's easier to break when maintaining the code. Example:
// BEFORE
$(document).ready(foo);
// AFTER: works
$(document).ready(foo).on("click", "a", function() {});
Contrast this with
// BEFORE
$().ready(foo);
// AFTER: breaks
$().ready(foo).on("click", "a", function() {});
Related to the above: ready is a freak in the sense that it's (the only?) method that will work the same no matter what the jQuery object wraps (even if it does not wrap anything as is the case here). This is a major difference from the semantics of other jQuery methods, so specifically relying on this is rightly discouraged.
Update: As Esailija's comment points out, from an engineering perspective ready should really be a static method exactly because it works like this.
Update #2: Digging at the source, it seems that at some point in the 1.4 branch $() was changed to match $([]), while in 1.3 it behaved like $(document). This change would reinforce the above justifications.
I would say its simply the fact that $() returns an empty object whereas $(document) does not so your applying ready() to different things; it still works, but I would say its not intuitive.
$(document).ready(function(){}).prop("title") // the title
$().ready(function(){}).prop("title") //null - no backing document
More than likely this is just a documentation bug and should be fixed, the only downside to using $().ready(handler) is it's readability. Sure, argue that $(handler) is just as unreadable. I agree, that's why I don't use it.
You can also argue that one method is faster than another. However, how often do you call this method enough times in a row on a single page to notice a difference?
Ultimately it comes down to personal preference. There is no downside to using $().ready(handler) other than the readability argument. I think the documentation is miss-leading in this case.
Just to make it patently obvious that there is some inconsistency in the three, plus I added the fourth often used form: (function($) {}(jQuery));
With this markup:
<div >one</div>
<div>two</div>
<div id='t'/>
and this code:
var howmanyEmpty = $().ready().find('*').length;
var howmanyHandler = $(function() {}).find('*').length;
var howmanyDoc = $(document).ready().find('*').length;
var howmanyPassed = (function($) { return $('*').length; }(jQuery));
var howmanyYuck = (function($) {}(jQuery));
var howmanyYuckType = (typeof howmanyYuck);
$(document).ready(function() {
$('#t').text(howmanyEmpty + ":" + howmanyHandler + ":"
+ howmanyDoc + ":" + howmanyPassed + ":" + howmanyYuckType);
});
The displayed results of the div from the last statement are: 0:9:9:9:undefined
SO, only the Handler and Doc versions are consistent with the jQuery convention of returning something of use as they get the document selector and with the Passed form you must return something (I wouldn't do this I would think, but put it in just to show "inside" it has something).
Here is a fiddle version of this for the curious: http://jsfiddle.net/az85G/
I think this is really more for readability than anything else.
This one isn't as expressive
$().ready(handler);
as
$(document).ready(handler)
Perhaps they are trying to promote some form of idiomatic jQuery.

jQuery Event handlers for Javascript functions

The question title is a bit obscure so let me explain.
A requirement that was only explained to me recently is the use of jQuery in my project. But it has been noted that my functions are fine as is and can be reused as long as they contain some jQuery.
So I'm exploring the world of event listeners for the first time (js side not in the HTML)
A standard Jquery onclick event:
referenceToElement.onclick = function () { alert('here'); };
One thing I notice is that the function doesn't actually have a name. Is there any clean way of doing something like:
referenceToElement.onclick = myOldJavascriptFunction();
function myOldJavascriptFunction()
{
//blahblahblah
}
Is this good practice or is there a better way to do it. Will this even work now that I think of it?
Even if the question is actually worth a downvote, since you could easily answer all those questions by searching, I'll give you a headsup.
That
referenceToElement.onclick = function () { alert('here'); };
is for sure no jQuery standard thing. It's pure Javascript, adding a property to a DOM reference, in this case an anonymous function. However, you basically asked two questions now.
can we give that anonymous function a name ? => YES
can we reference a function which is defined somewhere else ? => YES
To give it a name, we can just create a named function expression like this
referenceToElement.onclick = function myFunctionName() { alert('here'); };
To reference a function, we just pass in it's name
referenceToElement.onclick = myOldJavascriptFunction;
Finally, jQuery's syntax to add the same event listener would look like this:
$( referenceToElement ).click( myOldJavascriptFunction );
Yes, you were very nearly right:
referenceToElement.onclick = myOldJavascriptFunction;
Note the lack of parentheses. This passes a reference to the function to the onclick event. Also note that this is plain old JavaScript, not jQuery. The jQuery way of doing this is more like:
$(referenceToElement).click(myOldJavascriptFunction);
Your first example there is plain and normal javascript, nothing to do with jQuery at all. If you were using jQuery, the line would look like this:
$(referenceToElement).click(function () { ... });
But in any case, it seems like you question is about anonymous functions. You can assign the function to a variable name, or use a function declaration and still reference that function by name:
function myFunction () { ... }
$(referenceToElement).click(myFunction);

MOOTOOLS variable scope

I'm using mootools:
I can't figure out how to use a variable when using an addEvent.
I want to use a for next loop to set values in a loop:
for (x=0;x<num;x++){
var onclickText = 'function (){onclick="addPageMoveEvent('+x+'"); }';
$('pageNum'+x).addEvent('click', onclickText);
}
>
I've search forums but not found any help.
Any help would be great.
Thanks
The addEvent method in MooTools accepts two arguments:
myElement.addEvent(type, fn);
Arguments:
type - (string) The event name to monitor ('click', 'load', etc) without the prefix 'on'.
fn - (function) The function to execute.
It does not take a string and passing a string such as "myFunction()" or "function() { myFunction(); }" will not work.
Since you are inside a loop, and the variable x will share the environment, you need to wrap its value inside another closure. One way is to use an additional closure:
$("pagenum" + x).addEvent("click", (function(value) {
return function() { addPageMoveEvent(value); }
})(x));
See all questions on StackOverflow regarding this particular problem of creating closures within loops.
Also worth checking out is this MDC article - Creating closures in loops: A common mistake
Warning: this first example will not work! Read on for an explanation.
You are confusing onclick HTML syntax with the MooTools addEvent. Try
for (var x=0;x<num;x++){
$('pageNum'+x).addEvent('click', 'addPageMoveEvent('+x+');');
}
This is simpler and cleaner, but might still not do what you want. This code will call the function addPageMoveEvent every time the link is clicked... is that what you want?
Since MooTools doesn't allow the above method, you must use the following:
A programmatically more interesting and less hazardous way to do the same would be:
factory = function (x) { return function() { addPageMoveEvent(x); }; };
for (var x=0;x<num;x++){
$('pageNum'+x).addEvent('click', factory(x));
}
This uses a factory for creating closures that hold your values of x... rather complex code, but it's the purist way. It also avoids using the scary eval that occurs because you feed addEvent a string. (It seems that MooTools doesn't like the other option anyway.)
That a use case for mootools pass method.
for (x=0;x<num;x++){
$('pageNum'+x).addEvent('click', addPageMoveEvent.pass(x));
}
Pass internally creates a closure that holds x in the his scope, so when the click event is fired it has the right value cause its not the same from the for loop.

Can you do this using jquery?

Is it possible to do this from within a class?
$("#" + field).click(this.validate);
So basically I want to pass a function of the object that should be executed whenever something is clicked. Also, if there are more than 1 instances of this object, then the correct instance (i.e the one which runs this code for the given field) should be executed.
I am not sure about an easy way, but you can always go the closure route:
var that = this;
$("#" + field).click(function() {
that.validate();
});
Is it possible to do this from within a class?
$("#" + field).click(this.validate);
“this.validate” is problematic. JavaScript does not have bound methods, so when you pass that reference, it is only pointing to a plain function. When called, ‘this’ will not be correctly set. See ALA for a fairly thorough discussion of the binding loss problem.
Some frameworks provide built-in method-binding functionality; jQuery does not, as it tends to concentrate more on closures than JavaScript objects. In any case, creating a binding wrapper using a closure is pretty simple; Andrey's answer is the usual approach for jQuery users.
The one thing to look out for with closures for event handlers (whether you are using jQuery or not) is that it tends to cause memory leaks in IE. For simple, short-lived web pages you may not care.
Yes it is possible.
10 mins ago was writting a snipet because i had just that problem
$("div.myalarms_delete").click(function(){
var mid = this.id;
$("li#"+mid).fadeOut("fast");
});
the div.myalarms_delete also as the id I needed
From inside a class you're typically better off using the .find function.
var child = this.find('.someChildClass');
var child2 = this.find('#someChildId');

How to change onClick handler dynamically?

I'm sure there are a million posts about this out there, but surprisingly I'm having trouble finding something.
I have a simple script where I want to set the onClick handler for an <A> link on initialization of the page.
When I run this I immediately get a 'foo' alert box where I expected to only get an alert when I click on the link.
What stupid thing am I doing wrong? (I've tried click= and onClick=)...
<script language="javascript">
function init(){
document.getElementById("foo").click = new function() { alert('foo'); };
}
</script>
<body onload="init()">
<a id="foo" href=#>Click to run foo</a>
</body>
Edit: I changed my accepted answer to a jQuery answer. The answer by 'Már Örlygsson' is technically the correct answer to my original question (click should be onclick and new should be removed) but I strongly discourage anyone from using 'document.getElementById(...) directly in their code - and to use jQuery instead.
Try:
document.getElementById("foo").onclick = function (){alert('foo');};
Use .onclick (all lowercase). Like so:
document.getElementById("foo").onclick = function () {
alert('foo'); // do your stuff
return false; // <-- to suppress the default link behaviour
};
Actually, you'll probably find yourself way better off using some good library (I recommend jQuery for several reasons) to get you up and running, and writing clean javascript.
Cross-browser (in)compatibilities are a right hell to deal with for anyone - let alone someone who's just starting.
jQuery:
$('#foo').click(function() { alert('foo'); });
Or if you don't want it to follow the link href:
$('#foo').click(function() { alert('foo'); return false; });
I tried more or less all of the other solutions the other day, but none of them worked for me until I tried this one:
var submitButton = document.getElementById('submitButton');
submitButton.setAttribute('onclick', 'alert("hello");');
As far as I can tell, it works perfectly.
If you want to pass variables from the current function, another way to do this is, for example:
document.getElementById("space1").onclick = new Function("lrgWithInfo('"+myVar+"')");
If you don't need to pass information from this function, it's just:
document.getElementById("space1").onclick = new Function("lrgWithInfo('13')");
OMG... It's not only a problem of "jQuery Library" and "getElementById".
Sure, jQuery helps us to put cross-browser problems aside, but using the traditional way without libraries can still work well, if you really understand JavaScript ENOUGH!!!
Both #Már Örlygsson and #Darryl Hein gave you good ALTARNATIVES(I'd say, they're just altarnatives, not anwsers), where the former used the traditional way, and the latter jQuery way. But do you really know the answer to your problem? What is wrong with your code?
First, .click is a jQuery way. If you want to use traditional way, use .onclick instead. Or I recommend you concentrating on learning to use jQuery only, in case of confusing. jQuery is a good tool to use without knowing DOM enough.
The second problem, also the critical one, new function(){} is a very bad syntax, or say it is a wrong syntax.
No matter whether you want to go with jQuery or without it, you need to clarify it.
There are 3 basic ways declaring function:
function name () {code}
... = function() {code} // known as anonymous function or function literal
... = new Function("code") // Function Object
Note that javascript is case-sensitive, so new function() is not a standard syntax of javascript. Browsers may misunderstand the meaning.
Thus your code can be modified using the second way as
= function(){alert();}
Or using the third way as
= new Function("alert();");
Elaborating on it, the second way works almost the same as the third way, and the second way is very common, while the third is rare. Both of your best answers use the second way.
However, the third way can do something that the second can't do, because of "runtime" and "compile time". I just hope you know new Function() can be useful sometimes. One day you meet problems using function(){}, don't forget new Function().
To understand more, you are recommended read << JavaScript: The Definitive Guide, 6th Edition >>, O'Reilly.
I agree that using jQuery is the best option. You should also avoid using body's onload function and use jQuery's ready function instead. As for the event listeners, they should be functions that take one argument:
document.getElementById("foo").onclick = function (event){alert('foo');};
or in jQuery:
$('#foo').click(function(event) { alert('foo'); }
Here is the YUI counterpart to the jQuery posts above.
<script>
YAHOO.util.Event.onDOMReady(function() {
document.getElementById("foo").onclick = function (){alert('foo');};
});
</script>
I think you want to use jQuery's .bind and .unBind methods. In my testing, changing the click event using .click and .onclick actually called the newly assigned event, resulting in a never-ending loop.
For example, if the events you are toggling between are hide() and unHide(), and clicking one switches the click event to the other, you would end up in a continuous loop. A better way would be to do this:
$(element).unbind().bind( 'click' , function(){ alert('!') } );
Nobody addressed the actual problem which was happening, to explain why the alert was issued.
This code: document.getElementById("foo").click = new function() { alert('foo'); }; assigns the click property of the #foo element to an empty object. The anonymous function in here is meant to initialize the object. I like to think of this type of function as a constructor. You put the alert in there, so it gets called because the function gets called immediately.
See this question.
The YUI example above should really be:
<script>
YAHOO.util.Event.onDOMReady(function() {
Dom.get("foo").onclick = function (){alert('foo');};
});
</script>

Categories