Is it bad practice to dynamically set/change event handlers? - javascript

I am trying to replace confirm() boxes with bootstrap modals for save/delete operations in a page.There is a button(call it action-button) for each of these operation which then triggers the respective modal. On clicking the confirm button on the modal,the respective function is triggered.
Instead of writing modals for each of these operations,I was thinking if I could use just one confirm modal and change the event handler when the "action-button" is clicked.
Like this:
$("#confirm-button").attr('onclick',save_all());
when save_all button is clicked.
Is it bad practice?And what are some alternatives to this? Thanks!

I don't think changing the event handler is a good choice as you should handle the previous attached handlers in order to avoids memory leak and weird behaviours.
A better approach could be defining a generic handler that will read from a data attribute on the DOM, what to do in case of confirmation.
The main part is that such handler should not been coupled with the working staff.
To achieve this you could define custom events that will be fired from your generic handler.
I mean something like:
function confirmAction(ev) {
// you can save in the HTML the name of the proper event
var whatEv = $(this).data('my-key');
// Here you could fire a custom event.
$(this).trigger(whatEv);
}
Last thing, as #trincot pointed, your code in the OP is wrong.

It will not even do what you intend, because you call save_all at the moment you set the attribute, and it will be the function's return value that is stored in the onclick attribute, converted to string if it is not yet a string. This most probably is not what you intended to do.
Even if the attribute value would be correct, it is not the best practice, because:
That attribute value needs to be evaluated/parsed, which means you can get late syntax errors
It replaces the previous value of the onclick attribute, which might be an undesired effect (although I understand in your case that is what you want)
Instead bind event handlers the jQuery way and make sure you don't call the function, but pass its reference:
$("#confirm-button").on('click', save_all);
If the purpose is to replace any previous click handler, then chain in an off call:
$("#confirm-button").off('click').on('click', save_all);

Related

TypeError: $(...).on is not a function with prototype.js

I want to add a filtered listener to the change event of a form's children but I am getting weird results from the $ selector.
I call the selector with the id of the form $("exportForm") and try to call the .on(...) method on it, getting the error in question.
Inspecting the returned element I seem to find an array with numbers as ownProperties names. Indexing them in the console $(...)[1] returns the single children of the form. In the proto property there seem to be no trace of Prototype.js methods which should be added by the selector.
What is going wrong? What to look for to get it working?
PS: Prototype.js version is 1.6.1
You're right. on() was added in 1.7, so you can't use it here. What the on method gives you is "lazy evaluation". When you call
$(document).on('eventName', 'css selector', function(){ ... });
...you get an observer that doesn't have to be initialized after the page loads, or after parts of the page are replaced by Ajax callbacks. You can replace a part of the page, then click on it again, and the observer will just work.
The old way to set up an observer was this:
$(document).observe('dom:loaded', function(){
$('theId').observe('eventName', function(){
// do something
});
});
The outer observer waits until the page is finished loading, then the inner one observes some object for an event and does something with that. This will work, as long as the DOM doesn't change after it loads. If that happens, then you have to re-register the inner listener manually, since its targets have changed, and it no longer will work. The dom:loaded event only fires once, when the page itself is loaded the first time.
In your case, it seems that you want to duplicate the behavior of the Form.Element.Observer() class method in Prototype, which sets up a timed poll of all a form's child elements, and fires a callback when any of them change. You really ought to look at the documentation for that method, as it is a really bulletproof way to do what you're trying to do. But in case you really want to roll your own, here's how you would write an observer that could listen for events on multiple elements:
$(document).observe('dom:loaded', function(){
$$('#exportForm input').invoke('observe', 'change', function(evt, elm){
// evt is the event itself, you can cancel, log, whatever
// elm is a reference to the form element, you can do elm.getValue(), etc.
});
});
This uses the "double-dollar" method to get an array of elements, so any form input inside the form with the ID exportForm is captured.
invoke() applies the same callback and arguments to an array of elements, so this is setting up a separate observe method for each form element -- not efficient if you have lots of inputs! You can try to listen for the change event on the form itself, so you only have one observer method, but depending on the browsers you need to support, you may find that these events don't always bubble from the individual form input up to the form itself. You'll have to test carefully.
Each form element observed will be seen in isolation: you won't have access to the rest of the form inputs from within the callback, just that one.
That's why the Form.Element.Observer is so powerful -- it gives you a hash of the entire form each time it fires, so you can do things like create previews or validations on a frequent-enough basis to appear "live".

Triggering change on input

I have written some code that changes an input quantity on a magento 1.9 ecommerce website.
jQuery("input.qty").val("10");
The problem is the javascript that triggers the total to update doesn't fire. I have found the code responsible and it looks like this:
(function() {
var qtyFields = $('super-product-list').select('input.qty');
qtyFields.each(function(el) {
el.observe("change", updateGroupedPrice);
});
Event.observe(window, "load", updateGroupedPrice);
function updateGroupedPrice() {
//do stuff
}
})();
I think this is using prototype.js but I tried to isolate it in a codepen but couldn't get it working.
I have tried to trigger the change event like so:
jQuery("input.qty").trigger("change")
But it does not work. I also ran through a load of other events but in the dev tools it shows the code listening on "change".
Does anyone know why I can't trigger the change?
Since the page is using Prototype.js, you ought to keep using that to trigger your change. If you introduce jQuery into this, you're a) loading another complete duplicate of what Prototype already does, and b) asking for a lot of trouble isolating the fact that $() is a method in both libraries.
Your jQuery is a little fishy to me, too. You're setting the value of one picker (I imagine) and yet you are addressing it with a classname, so potentially there is more than one select.qty in the page, and all of them will change to value 10, firing off (potentially) multiple callback functions.
The Prototype code you see here is setting up a "listener" for changes on what you would address in jQuery as$(#super-product-list input.qty) inputs.
jQuery always treats $() as returning an array of objects, and thus all of its methods act on the array, even if it only contains one member. Prototype has two different methods for accessing elements in the DOM: $('some_id'), which always returns one element (or none, if no match), and $$('some css selector'), which always returns an array (of zero or more matching elements). You would write (or use native) callback methods differently, depending on which accessor you used to gather the element(s).
If you want to change one of these inputs, you will need to isolate it before you set its value.
Let's say there are three select pickers with the classname qty in your #super-product-list element. You want to change the third one to 10:
$('super-product-list').select('input.qty').last().setValue('10');
Or, much smarter than this, you add an ID to the third one, and then your code is much shorter:
$('quantity_3').setValue('10');
In either case, this will send the "change" event from your select, and the updateGroupedPrice method will observe that and do whatever you have coded it to do.
You won't need to (and should not ever) trigger the change event -- that's a "native" event, and the browser owns it. jQuery's trigger() (which is fire() in Prototype, is used exclusively for "synthetic events", like you see in Bootstrap: show.bs.modal, hide.bs.modal, etc. You can spot these by the punctuation in their names; usually dots or colons to namespace the events and avoid collisions with other code.
Finally, if you really, really, really wanted to change every single #super-product-list select.qty element on the whole page to '10', you would do this in Prototype.js:
$$('#super-product-list select.qty').invoke('setValue', 10);

Event Listener or onClick - Best Practice

I have an HTML page with around 100 text boxes. I currently use a JS event listener (blur) to detect when a user leaves a text box. Within the event listener I use if statements to filter the action depending on which text box was used, i.e. if id == blah, if className == blahblah, etc.
Would it be considered a better practice to place onBlur() in the HTML tag of each text box in order to direct the action to specific functions, or am I on the right track?
Hey buddy as rightly told by Sergio. Its better to have event handlers in script element by DOM approach.
Now to simplify your task use EVENT DELEGATION
just attach document.body.onchange=detect
function detect()
{
var textboxvalue=event.target.value //event.target point to text box on which blur event fires
}
This simple code will do your task by bubbling of event. No need to use any arrays/conditionals/classes/ids/seperate event handlers
"change" event is same as "blur" event ."change" only fires if value of textBox has been changed so null/empty string is not applicable
Productivity is priority after performance, for me.
Since you're new, I would recommend a javascript library like jQuery. With this, you can do a simple statement to put the blur/keyup/change events on all, and handle it in one organized place.
I have no doubt this can be done with pure javascript, but a major benefit of a javascript library is the increase in productivity by writing fewer lines.
In your specific case, I would do a jQuery $.keyup() on all <input>s via an empty css class and if or switch off another empty class to specify sub-textboxes:
$('.myEmptyClassThatsOnEachOfThe100Textboxes').keyup(function(){
if($(this).hasClass('myEmptyClassThatsOnEachTextboxOfThisFirstType')){
//do stuff
}
else if(...
})

what's the difference between <a onclick="someFunction"> and <a onclick="someFunction()">

what is the difference between
<a onclick="someFunction">
and
<a onclick="someFunction()">
One uses the parenthesis and not the other, but what are the differences of using either? What is the "correct" option? And what happens if i dont use any href attribute?
As far as I know, in javascript, using something = someFunc(); assigns the return value of that function to the something variable. And using something = someFunc; assigns the function directly (not its result) to that variable (And it's mostly used to assign functions to events). e.g. I can assign a function to a onclick event.
But what I don't understand is what happens when using either in some html element inline event, as in the examples, since the assignation is not to a javascript variable, but to an html attribute, which happens to be an event? Please explain.
And also, is there a difference on assigning a inline onclick function to an anchor (a) that to other elements (e.g. span div label etc)? Do they have the same effect?
Sidenote:
I've been reading here about how to run a function when clicking on a link, and I already understood is that is should not be done "inline", but instead using unobtrusive javascript. (I mention it to avoid debate about that), but in the examples I've seen they don't mention the difference of both options I mention when doing it inline.
Edit: This question was made because here they gave an answer which doesn't use the parenthesis in the function for the event, and nobody mentioned the parenthesis were needed, so I assume it is valid. yet I don't know what is the difference of using () or not.
One uses the parenthesis and not the other, but what are the differences of using either?
<a onclick="someFunction"> won't do anything. The parenthesis cause a function to be called.
Having a statement consisting of nothing but an identifier (be it a function name, variable, or whatever) won't do anything (except throw a reference error if the variable doesn't exist).
And what happens if i dont use any href attribute?
Then I'd question why you were using an <a> element in the first place.
And also, is there a difference on assigning a inline onclick function to an anchor (a) that to other elements (e.g. span div label etc)?
Only that they aren't (by default) focusable elements (nor is an a element without an href attribute), so the click event couldn't be triggered by tabbing to the element and pressing enter. If you want an element that will do something with JS when triggered, and you don't have a sensible fallback for when JS isn't available, use a button.
The value of an event handler attribute is a sequence of Javascript statements, not an expression.
It isn't assigning a function value to the property; it's a piece of code to execute at that event.
Leaving out the parentheses, results in an expression statement that has no effect.
when writing inline on click functions, we assigning the code to be executed in the form of string on click of the element.
It is equivalent to eval('someFunction()');
we cannot write on click='someFunction' since it will be equivalent to eval('someFunction') which would do nothing.
if you intend to bind a click handler to an anchor tag, dont forget to add a href='#' attribute to the anchor tag.
There is no difference between assigning a click handler to span or divs as compared to anchor tag.

Are defining many jQuery event handlers "expensive?" Internally Implemented at C/C++ or JavaScript level?

I have a long a web form. I'm wondering if I should bind() to each <input> element separately (what I really want) or should I only define one bind() all of the input elements then do an if-then inside the handler to handle the specific element?
jQuery's event handlers are implemented in JavaScript (they have to be).
In general, it's best to use a single handler for the entire form, rather than a large number of individual elements. But jQuery's live feature can help quite a lot, it does a lot of the plumbing for event delegation for you. For example:
$("#formid input").live('focus', function(event) {
var field = $(this);
// `field` now references the field that was focussed
});
...watches all fields in your formid form for focus events. If you're using jQuery 1.4, that works even with the focus event in that example (even though focus doesn't bubble, and so is usually tricky to use with event delegation). Prior to 1.4 that won't work, but bubbling events like click and such do.
I would bind to all the inputs, but you can use classes to narrow it down to just the inputs you want.
Instead of
$("#formid input").click(function() {
if($(this).attr('something') == 'special_field') {
//do A
}
});
add a class to the elements you are interested in <input class='special_field' ... >, and change your selector:
$("#formid input.special_field").click(function() {
//do A
});
That will limit the handler to only the desired elements without any if statement checks.
You can repeat this for anything that needs special handling.
Final note on the .live() method. It can be really handy for a couple reasons:
It doesn't actually create a binding for every matching element, it creates a listener on the body element by default(you can choose another element as the listener) and waits for an even to bubble up to it. When it bubbles, it checks the triggered element against the selector, and if its a match fires the event.
Because the selector is applied when the event fires, it means that new elements added via Javascript or AJAX will fire the code without needing to bind or rebind them.
There is one listener for many elements, meaning for large sets it can be much quicker.

Categories

Resources