$(".class").width($(this)...) not working - javascript

I have a lot of divs with, let's say, the .class class. And I need to assign them a width based on an specific route
$(".class").width($(this).parent().next().find("code").width())
But, for some reason, it doesn't work with $(this) but it does when I do this:
$(".class").width($(".class").parent().next().find("code").width())
but obviously only for the first one. Any clue?

jQuery width() method accepts a function:
A function returning the width to set. Receives the index position of
the element in the set and the old width as arguments. Within the
function, this refers to the current element in the set.
$(".class").width(function () {
return $(this).parent().next().find("code").width()
});

Since classes are meant to be used on multiple elements, your jQuery selector is probably returning a set of elements rather than a single element. Additional jQuery operations such as .width() on a set of matched elements are only executed on the first match. Therefore you'll need to iterate over the matched set of elements in order to complete the function for each match.
$('.class').each(function (idx, elem) {
// Returns the width
// Not sure what the intention is here. Assign to variable maybe?
return $(elem).parent().next().find('code').width();
});

The way you are calling these functions, there is no reason why this should point to each individual .class elements. You are in global context (of whatever), but not in the context of .class DOMElement.
You can use each method instead:
$(".class").each(function() {
$(this).width($(this).parent().next().find("code").width());
});
or width method providing a function as a setter:
$(".class").width(function() {
return $(this).parent().next().find("code").width();
});
Both methods will loop through every element in collection $('.class') and invoke callback functions in the context of every element.

Related

Order of Execution of Modifications on Selection

In d3.js, numerous calls to modify aspects on all elements in a given selection can be chained like so:
mySelection.attr('someAttribute', function retrieveSomeAttributeValue (d) {
return d.someValue;
}).style('someCssProperty', function retrieveSomeCssPropertyValue (d) {
return d.someValue % 2;
});
Now, both retrieveSomeAttributeValue and retrieveSomeCssPropertyValue will be called once for each element in mySelection.
However, is their order of execution for any given element also specified? I could not find anything explicit on this in the docs. Concretely: For any given element in mySelection, is retrieveSomeAttributeValue guaranteed to be called before retrieveSomeCssPropertyValue because the attr call surrounding the former precedes the style call surrounding the latter in the call chain?
In general terms: Are modification functions such as attr and style applied in the order they are chained?
EDIT: As there seems to be some confusion about the scope of my question, I will try to clarify: Obviously, the attr function is invoked before the style function. But does attr apply its changes to each element, and then return the resulting collection, whereupon style applies its changes to each element? Or do, for instance, attr and style somehow add their respective modifications as pending changes on the collection object, to be executed in a possibly different order at a later time?
EDIT2: As this question appears to generate some confusion, please use the comments area to ask for clarification if anything in particular is unclear.
When it comes to fiddling with the DOM tree D3 tends to be implemented pretty straightforward without having to resort to any kind of hidden magic. Many calls to D3's methods will more or less directly delegate to native DOM interfaces. Methods like .attr() and .style() are no exception to this general assessment.
Since documentation may hide the inner workings of a library from its reader and user, the best way to assure yourself of the way D3 does this stuff is probably to dig into its source code. As an example let us have a look at selection.attr().
According to the docs:
# selection.attr(name[, value]) <>
[…] otherwise, if the value is a function, it is evaluated for each selected element, in order, being passed the current datum (d), the current index (i), and the current group (nodes), with this as the current DOM element (nodes[i]). The function’s return value is then used to set each element’s attribute.
This describes the method's usage as a setter by providing two arguments to the call: the first being the attribute's name, the second being the callback. The relevant part of the method's full source look like this:
return this.each((value == null // <== For all nodes in this selection...
? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function"
? (fullname.local ? attrFunctionNS : attrFunction) // <== ...set to callbacks return value.
: (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));
Based on how to handle namespaces (fullname.local) it will call the appropriate function attrFunctionNS or attrFunction. Both of which look quite similar:
function attrFunction(name, value) {
return function() {
var v = value.apply(this, arguments); // Evaluate the callback.
if (v == null) this.removeAttribute(name); // Standard method call.
else this.setAttribute(name, v); // Standard method call.
};
}
The function returned from this call will execute the callback (referred to by value) on its first line. Based on the value returned from that call it will either remove the attribute if that value equals null or it will set the attribute's value to the callback's return value. Both these operations make use of the two standard DOM API methods Element.removeAttribute() or Element.setAttribute(), respectively.
As can easily be seen from these few lines, D3 does not make any use of hidden magic or intrinsic state but directly forwards to native DOM methods. For this same reasion you are guaranteed that your callbacks will get called for every node in the selection in the very same order as they are chained.
Always because a call to attr(), style() , append(), filter(), .... return a selection that the next call operates on. This selection can be different of the selection the call operates on.
Before the return the callbacks (if defined) are called for every element in the selection. There is no asynchronous stuff happening behind the scenes.

What's the difference between an object directly queried and an object querying one of its children?

When I add a class on the following object, everything is fine:
$('.select').on('click', function() {
$(this).addClass('active');
});
But when I try to add a class to a child, I get an error:
$('.select').on('click', function() {
$(this).find('.list')[0].addClass('active');
});
From my research I've learned that certain objects have specific methods that can be called on them. So there must be a difference between
$(this)
and
$(this).find('.list')[0]
But I can't figure out what the difference would be, and I don't know how to find that out.
The difference is that $(this)is a jquery object, while $(this).find('.list')[0] is a DOM element, and doesn't have functions and methodsprovided by jQuery.
Because calling $(this).find('.list') will give you a nodeList and when you call [0] you will get the first node object from this list, and a node or a DOM element is different from a jquery object, as jQuery wraps a DOM elemnt with its utility methods and attributes.
So what you need to do is to convert this DOM element to a jQuery object so you can call jQuery methods on it:
$($(this).find('.list')[0])
$(this).find('.list')[0] returns a dom element from the jQuery object.
Elements don't have an addClass() method and you should be seeing an error in browser console telling you that
If you want the first one you can use first() or eq()
$(this).find('.list').first().addClass('active');
// OR
$(this).find('.list:first').addClass('active');
// OR
$(this).find('.list').eq(0).addClass('active');

JQuery and JavaScript methods

In majority of JavaScript method(iterator) callback first argument is element and second is index. But in case of jQuery always second argument is element and first one is index. Why they formatted differently? Is that for avoiding confusion between them, since both contains methods like map, filter, etc. Is there any special meaning behind that? I'm just curious to know.
This is because jQuery relies on this; it doesn't need an element iterator.
$('selector').each(function(){
// 'this' refers to unwrapped jQuery selector element; no need for arguments == cleaner code
var $this = $(this);
});
In Vanilla JS, iterators rely on the parameters:
[].forEach(function(el, index){
// 'this' refers to Window object; use the argument
el.querySelector('foo')
});
Because jQuery can utilize the scoped element using this, it doesn't really need the arguments. I'd have to look up the history of the library to even see if the element existed as the second argument in earlier versions.

jQuery - equivalent to each(), but for a single element

What's the jQuery equivalent for each():
$(".element").each(function(){
// do stuff
});
when attaching a function to a single element, like #element ?
You can always reference the jQuery object in a variable:
var $el = $('#element');
...then manipulate it.
$el.doSomething(); // call some jQuery methods from the cached object
$el.doSomethingElse();
If the reason you wanted .each() was to reference the DOM element as this, you don't really need the this keyword to do it, you can simply grab the DOM element out of the jQuery object.
var element = $('#element')[0]; // both of these give you the DOM element
var element = $('#element').get(0); // at index 0
The two of these are equivalent, and will retrieve the DOM element that would be referenced as this in the .each().
alert( element.tagName ); // alert the tagName property of the DOM element
alert( element.id ); // alert the ID property of the DOM element
I'd note that it isn't necessarily bad to use each to iterate over a single element.
The benefits are that you have easy access to the DOM element, and you can do so in a new scope so you don't clutter the surrounding namespace with variables.
There are other ways to accomplish this as well. Take this example:
(function( $ ) {
// Inside here, "this" will refer to the DOM element,
// and the "$" parameter, will reference the jQuery library.
alert( this.tagName );
// Any variables you create inside will not pollute the surrounding
// namespace.
var someVariable = 'somevalue'; // is local to this function
}).call( $('#element')[0], jQuery );
To directly answer your question, .each() operates normally on element sets of any size including 1.
You can also omit the .each() call completely and just call jQuery methods on $('#element'). Remember that you can chain most if not all jQuery method calls as they return the jQuery object. This even works on multiple elements for the matter, depending on what the methods do.
$('#element').doSomething().doSomethingElse();
If you need to reference the object multiple times, make a variable:
var $elm = $('#element');
$elm.doSomething();
doSomethingElse($elm);
Use first().
each() matches all elements, while first() matches only the first.
There are other selectors too. When you use the id in the selector, you will only get one element. This is the main difference between .element and #element. The first is a class that can be assigned to many elements, while the second is an id that belongs to only (at most) one element.
You can still use each if only one (or 0) element is returned. Also, you can skip each altogether if you want to link an event. You use each when you want to execute a specific function for each element in the list of elements.
If there is only 1 element, you can access it normally using the selector.
$('#your_element').your_event(function() {
});
Behind the scenes, each is just a for loop that iterates through each element in the map returned by jQuery.
It is essentially† the same as:
var i, map = $('...selector...');
for (i = 0; i < map.length; i++)
{
someFunction(i, map[i]);
}
† There's more to it than this, involving calling the function in the context of the map element etc.
It's implementation is to provide a convenient way to call a function on each element in the selection.
Do you mean like $('#element').children().each()
supposing you have something like a ul with an id and you want the li each inside it?
If the intention is to call a (non-jQuery) function in a new scope I think using "each" still is a valid (and probably the most elegant) way and it stays true to the jQuery syntax although I agree to Alex, it feels wrong since it might have some overhead.
If you can change the syntax use $('#element')[0] as replacement for this (as already mentioned in the accepted answer).
Btw could someone with enough reputation please correct the comment of "zzzzBov" about the accepted answer?
$('#element')[0] and $('#element').get(0) ARE! the same, if you want the jQuery object use $('#element').first() or $('#element').eq(0)

How to set callback on single items using jQuery

I have a js function which has, until now, always been the callback for a click event, and therefore relies heavily on the 'this' pseudo-variable. 'this' is a <li> element.
However I now have a circumstance where it is sometimes triggered using more convoluted route, and in these circumstances 'this' is an entirely different element. However, before calling the function I am able to find the relevant <li>, but is there a way I can feed this in as 'this'? I thought of using .each() on the <li>, but it won't work on a single element.
edit it turns out that each() does work on single elements.. the error turned out to be something else.
Haven't deleted this question though as it could be useful to others
You are looking for the call method:
function onClick() {
console.log(this); // will be the #test element in both cases
return false;
}
$('#test').click(onClick);
$('#test2').click(function() {
onClick.call($('#test')[0]);
return false;
});
Although this is also possible with apply:
onClick.apply($('#test')[0]);
The [0] is necessary to pass the actual DOM element instead of the jQuery wrapped set.
try using jquery.callback plugin. It keeps the context of callback.

Categories

Resources