I have several <select> boxes all using the same prefix and I would like to set up a recursive function to essentially do the work for me.
$('[id^="by_"]').change(function(e)
{
var elem = e;
console.log(e.value);
});
Based on this code is my intention pretty clear? Am I on the right track?
console prints out: undefined
I think you're on the right track - the selector you're using matches a prefix of "by_", and you're binding the change event to all of them. Make sure you put this in $(document).ready or similar. Are you having any problems with this code? Instead of using the e parameter, I would just use this inside of the function to refer to the element and $(this) to get the jQuery object of it. So to get the value, you'd use:
this.value
// or
$(this).val()
(ignore the e and elem stuff, although it wouldn't be a bad idea to store $(this) in something like elem so you can have a reference to it instead of re-creating the jQuery object every time you need it)
When using callbacks to events with jQuery, the (first) parameter of the callback is an event object that explains many things about the event that occurred ( http://api.jquery.com/category/events/event-object/ ) and does not hold the element - that's what this is for!
e in your code is the event object which has no value property, you should use this instead:
$('[id^="by_"]').change(function(e) {
var elem = this;
console.log(this.value);
});
Or if you want to use event object, you can use target property:
e.target.value
Since you're already using jQuery, why not something like this:
$('[id^="by_"]').change(function(e)
{
var $elem = $( this );
console.log( $elem.val() );
});
Isn't it more something like that:
$('[id^="by_"]').change(function()
{
console.log($('option:selected',this).val());
});
jsfiddle
Related
I am trying to perform an action to other elements than the $(this) item
$('.items').click(function(){
var myitem = $(this);
$(".items").each(function() {
if (myitem == $(this)){
break;
} else {
//perform action
}
});
});
Where did I go wrong? Is there any better method?
Try to use the .not() function to filter out the current element,
$('.items').click(function(){
$('.items').not(this).each(function(){
//perform action here.
});
});
What went wrong?
When using the jQuery method (a.k.a. $) a new instance of the jQuery object is created, containing a list of elements matching your selector along side with a rich prototype of jQuery's methods.
Your mistake was to try and compare two different instances.
What you could have done was to compare the elements themselves by making the following changes:
// change this:
var myitem = $(this);
// to this:
var myitem = this;
// change this:
if (myitem == $(this)){
// to this:
if (myitem == this){
Unless you intend to use the jQuery object functionality there's no reason to initiate a new instance. Simply use the element itself when possible. It's a best practice to avoid such use cases. Performance wise.
Best solution
But the best solution in your case is what was mentioned in all other answers, using jQuery's not method to exclude the element from the newly created instance.
Using.not() to avoid
Try this
$(".items").not($(this)).each(function() {
});
OR
As per your code
$(".items").not(myitem).each(function() {
});
you can use not() to ignore the element which is clicked:
$(".items").not(this).each(function() {
});
I have legacy code that includes links with hard-coded onclick handlers (bad, I know).
link
I need to decorate that onclick function. In other words, I need to to replace the hard-coded onclick function with another function of my own that will first call the original function and then allow me to call my own code.
This is what I've tried, to no avail:
var oldFunc = $('a.linkClass').attr('onclick');
function newFunc(oldFunc) {
return function(oldFunc) {
oldFunc();
// my add'l code
}
}
$('a.linkClass').attr('onclick', '').unbind('click');
$('a.linkClass').click(newFunc(oldFunc));
First issue is need proper selector to find the onclick elements. $('[onclick]') will find all elements in page with that attribute. Also you need to remove the onclick attribute, trying to unbind with jQUery won't work
Assuming each onclick only has one function you could try:
$('[onclick]').each(function(){
var $el=$(this)
var funcName=$el.attr('onclick').replace('()','');
$el.click(window[funcName]).removeAttr('onclick');
});
Just realizing this won't work if any params in function
You are trying to wrap multiple events, but to do that you need to loop over the elements and wrap each one in turn (javascript can't store multiple functions in a single variable like that!) Also you get a string from onclick, so you'll need to eval it (which is generally a BadThing (tm). But the real bad thing here is that you're using onclick in the first place.
var elementsToFix = $('a.linkClass');
for( var i = 0; i < elementsToFix.length; ++ i ) {
var obj = elementsToFix.eq( i );
var oldFnc = obj.attr( 'onclick' );
obj.removeAttr( 'onclick' ).on( 'click', (function( curOldFnc ) {
return function( ) {
// pre-wrapper here
eval( curOldFnc );
// post-wrapper here
}
}( oldFnc )) );
}
This uses the super-evil eval. That's bad, and the only reason I'm using it is because you get a string from attr('onclick') instead of a function. The correct solution is to never use onclick in the first place.
jsfiddle example: http://jsfiddle.net/Jdn6V/1
As the function called from the onclick handler is in the global scope, the easy solution would be to just remove the onclick handler, store the string, and eval it later.
Of course eval is bad, and there are ways to parse the string, remove the parenthesis and keep any arguments, and call it as window[oldFunc] etc. but as a quick example, here it is with eval (which is evil)
$('a.linkClass').each(function() {
var old = $(this).attr('onclick');
$(this).attr('onclick', '').on('click', function(e) {
e.preventDefault();
eval.apply(window, [old]);
alert('new');
});
});
FIDDLE
without eval
$('a.linkClass').each(function() {
var old = $(this).attr('onclick'),
func = old.split('(')[0],
param = old.split('(')[1].replace(')','').split(',');
$(this).attr('onclick', '').on('click', function(e) {
e.preventDefault();
window[func].apply(window, param);
alert('new')
});
});
FIDDLE
.attr() always returns a string, so you'd have to eval it to execute the function.
You can get the original function this way, and not need to eval it later:
var oldFunc = $('a.linkClass')[0].onclick;
([0] picks the "pure" DOM element that has the onclick function.)
Now you can bind the function to the element:
$('a.linkClass').click( function(e) {
// new code here
// ...
oldFunc(e);
});
(e given as the parameter, just in case the old function uses it.)
The additional benefit of this is that it works regardless of what's in the old onclick attribute because it's wrapped in a function, so e.g. onclick="foo(); bar(); return false;" works correctly.
I'm reformatting some really bad HTML using jQuery. I need to splice sibling <font> elements together. I tried this code:
$('font+font').each(function() {
this.html().appendTo( this.prev() );
this.remove();
});
but it gave me this error: TypeError: 'undefined' is not a function (evaluating 'this.html()')
Here's a sample of the HTML:
<font>This fragment </font><font>is actually one element.</font>
Update
I updated my code with $(this), but it still isn't working. When I run this code
$('font+font').each(function() {
$(this).html().appendTo( $(this).prev() );
$(this).remove();
});
I get this error: TypeError: 'undefined' is not a function (evaluating '$(this).html().appendTo( $(this).prev() )')
this has to be wrapped in a jQuery object, before you can use jQuery methods on it.
.html() returns a string. You cannot use jQuery methods on a string without wrapping. Use $this.prev().append( $this.html() ) instead.
When using $(this) more than once, it's wise to store $(this) in a temporary variable. It's the convention to prefix jQuery objects with a dollar sign.
Code:
$('font+font').each(function() {
var $this = $(this);
$this.prev().append( $this.html() );
$this.remove();
});
When you use an each statement, it returns this as a DOM element, not a jQuery object. .html() must be called on a jQuery object. So the first part of your fix is to convert this into a jQuery element with the $ symbol.
$(this).html(); //don't use this.html();
The second problem is that html() returns a string. You can't call AppendTo() on a string, only a jQuery object. Since you are working with .html() I'll assume that you want the string contents and not the full contents. If that were the case, Rob's answer is more appropriate.
this.textContent = $(this).prev().html() + this.textContent; // prepend siblings content
The final code ends up looking like this:
$('font+font').each(function() {
this.textContent = $(this).prev().html() + this.textContent;
$(this).prev().remove();
});
http://jsfiddle.net/b6vLL37k/1
You need to use $(this) not this
You need to use $(this) for jQuery to help you.
I wasn't able to fix your code. How about something like this:
var text = '';
$('font').each(function() {
text += $(this).text();
});
console.log($('<p />').text(text));
What I want to do:
( clickedObject === someDiv ) //returns true or false
What I tried
( $(e.target) === $('.selector') ); //returns a false negative.
My workaround
( $(e.target).attr('class') === $('.selector').attr('class') ); //works as intended, not so clean though.
What is the right way to compare the object I clicked to an object in the DOM?
To check if e.target has this class you can use the hasClass function.
if ($(e.target).hasClass("selector"))
Or, if you really want to compare objects, note that jQuery selectors return a collection of items, so I think you'll want
if (e.target === $('.selector')[0])
You're close. Use .is() instead:
if($(e.target).is('.selector')) {
// Your code
}
The trick here is that you wrap e.target in a jQuery object to allow it access to all the useful jQuery methods.
If you're just seeing whether e.target has a certain class, try using .hasClass() in place of .is():
if($(e.target).hasClass('selector')) {
// Your code
}
Either method works, although .hasClass() is a little clearer as to what the code does, and is faster than using .is()
If you want to match the element that the event is attached to you can use $(this), or if you want to find which element triggered the event use $(event.target).
Below is an example of both of these.
http://jsfiddle.net/Phunky/TbJef/
Unless you're using event delegation these will be the same though and if there the same element.
Obviously using .is() function is the best solution here.
If you find yourself doing such comparison, try to check if it is possible to use embedded jQuery mechanisms like this:
$(element).on("click", ".selector", function() {
alert("clicked");
});
Second argument in the .on() method is a target selector. When using this construction (read more: http://api.jquery.com/on/#direct-and-delegated-events) there will be no need to make any additional comparisons.
https://jsfiddle.net/m5zysufy/
$(document).click(function (e) {
e.stopPropagation();
var container = $('.dropdown-list').attr('class');
if ($(e.target).attr('class') == container) {
$('.dropdown-menu').slideToggle();
} else {
$('.header .header-elem .dropdown-nav .dropdown-menu').slideUp();
}
});
I am trying to attach an onChange callback to all the input elements under the div #dim. It selects all 3 input elements, but returns an exception:
Uncaught TypeError: Object 0 has no method 'change'
It may be because x may not be a jQuery object. How would I make this work?
function registercb() {
var sel = $("div.dim > input");
for (x in sel) {
x.change(function() {
dosomething();
});
}
}
You can simply do:
function registercb() {
$("div.dim > input").change(dosomething);
}
A few things to watch for:
Inside that iteration (don't use this, see the next point) x is the DOM element, not a jQuery object which has the .change() method, you would need to wrap it in a jQuery object like $(x), but again that isn't the correct solution here.
Don't use a for(...in...) loop to iterate an object (a jQuery object is array-like), that type of loop is for enumeration.
Most jQuery functions (almost all) run on more than one element, so just run it on the set to affect all elements, .change() is one of these.
In the cases you do need to loop, check out .each(), as it'll make your life much easier, don't use this here, it's only an example of what it would look like:
Example:
function registercb() {
$("div.dim > input").each(function() {
$(this).change(dosomething);
});
}
You don't have to loop over the elements. You can think of a jQuery object as holding a collection. When you do:
var sel = $("div.dim > input");
It means that sel has all the input elements in it, so then when you run a method like change() it will affect all of the elements in the collection. Thus you can do something like this:
function registercb() {
$("div.dim > input").change(function(){
dosomething();
});
}
Bonus knowledge: Now your problem is that when you were doing for( x in sel ) you are getting a lot of stuff on the jQuery object itself that you don't want. If you run the following code in chrome you'll see it outputting a lot unexpected stuff:
for( x in sel ){
console.log( x );
}
Instead jQuery has the each that lets you loop over the things you want:
sel.each(function(index, item){
console.log(item);
});
You can even use it on other things, which is really handy!
$([1,2,3]).each(function( index item ){
console.log( item ); // 1,2,3
})
Assuming your 'dim' div has an ID rather than a class of dim, you can simply do this:
$("#dim > input").change(function() { dosomething(); });
Working example.
In the text you refer to #dim whereas in the code you're refering to .dim - # selects by ID and . selects by class, so if your div is in the format <div id="dim"> then you won't find any matched elements with div.dim as your selector.