jQuery each() behavior if changing DOM within loop - javascript

What is the behavior of jquery when using each() loop? Is it:
look for the first object, execute function, look for the next object...
gather all objects in a container, then execute the function on each of them
other (what exactly?)
An example, where it is relevant:
<div id="a">
A
<div id="b">
B
</div>
</div>
<div id="c">
C
</div>
If I execute this javascript:
$('div').each(function(index){
alert($(this).html());
$(this).remove();
}
Will I see three alerts or only two?

Regardless of what action you will perform on a selection, jQuery will make that selection before applying that action. By which I mean $('div') is a selector, the selection process happens before and regardless of the other chained methods (such as each()). This is a product of the language, since $() must be evaluated before a method can be called upon it.
If that selection grabbed three divs from your page, then there are now 3 jQuery objects in a list ready to be iterated over. You can prove this by doing:
$('div').length
Thus you are iterating over an array with three indexes (0, 1, 2), if you remove the div from the DOM for index 1, the next iteration of the each() callback will still be for the object at index 2. Checkout this live demo for proof:
DEMO: http://jsfiddle.net/marcuswhybrow/HYJa4/

jQuery will behave as you've described in #2. You'll see three alerts:
"A <div> B </div>"
"B"
"C"
However, since div B is inside div A, it will be removed with div A. jQuery still knows about it, and will alert its contents, but it will have already been removed by that time. So, although you get three alerts, you only have two removes (since it removes child elements).

2 is the answer.
jQuery gathers a list of matching elements, then loops over the list.
One important think though, do not remove any element that can be mached that has not already been processed or you will risk getting a reference not found error.

Related

Updating DOM (JavaScript)

I have a problem with updating DOM. Probably it's not hard to do for experienced developers.
I made a carousel with 4 items and all of them are shown by default.
For example, when clicking on the < LEFT ARROW I need to replace the first item with the next one, and the first one must be pushed as the last one. The same logic for the right arrow >
I get my carousel like this:
const carouselItems = Array.from(document.querySelectorAll('.carousel-item'));
Also the function for replacing the items:
function carouselToLeft() {
carouselItems.push(carouselItems.shift());
}
So my caroueslItems array changes as expected but I can't make the DOM to update. Somebody help with this please. I'm not experienced as you guess, but I need the answer for now.
Here is a solution with using only a single constant and no variables or arrays:
const cont=document.querySelector(".container");
document.querySelectorAll("button").forEach(b=>b.onclick=ev=> // assign click events
cont[(b.textContent=="<"?"pre":"ap")+"pend"]( // prepend() or append()
cont.children[b.textContent=="<"?cont.children.length-1:0]) // last or first child-element
)
<h2>A simplified carousel</h2>
<div class="container">
<div class="carousel-item">one</div>
<div class="carousel-item">two</div>
<div class="carousel-item">three</div>
<div class="carousel-item">four</div>
<div class="carousel-item">five</div>
</div>
<button title="move last element to the top"><</button>
<button title="move first element to the bottom">></button>
The reason why I get away with so little code is that when we append() or prepend() a DOM element it will be taken away from its original position. So, no need to "clean up". And by looking freshly at the children-collection of .container I am always up-to-date with the current state of the carousel.
Array.from create a new Array Object that whatever you do on it can make DOM update itself.
You can get an Array-like object by using document.querySelectorAll, then push each item of it to a true Array.
Then, while you want to replace the first item with the next one, you can remove it, then append to the parent element of it.

jQuery click action stripped out by sorting function

I have a function, including the following code:
$(list).append(item);
$(list).html(
$(list).children("li").sort(function (a, b) {
return $(a).val() - $(b).val();
}));
$(item).click(this.listClick);
(Basically, it is creating a <ul> <li> from an array of items, including a listClick function which fires when any of the list items are clicked, and then sorting the list according to the li value).
If I strip out the sort function from the script, then it works fine (as in, the list is compiled, with click-function working perfectly, albeit not in the correct order). However, if I sort the list, it strips out the click-functionality (i..e. clicking the items doesn't perform the listClick function).
Why is this being stripped out? It doesn't seem to matter whether I place the sort function before or after the listClick line is added.
That's probably because you are using html method which replaces html content of the element and has nothing to do with the sort method. The old elements are removed and their attached event handlers too. You should either use event delegation or better use appendTo instead of html:
$(list).children("li").sort(function (a, b) {
return $(a).text() - $(b).text();
}).appendTo(list);
As the appendTo only moves elements, the event handlers are preserved.
Also note that li element doesn't have value property so .val() method returns an undefined value. You should use text method for getting textContent of the element. And if list is already a jQuery object there is no need to wrap it with jQuery constructor.

Access to object property or method from appended element jquery

I would like to know if it's possible to access an object property from an appended element. For example:
function anyFct(){
this.div=$('<div ref="dv">').html('Hi').appendTo('body');
div.animal='dog';
div.yld=function(){
alert(div.animal);
};
$('input type="text" value="anyIn" onclick="yeldAnimal(this);"').appendTo(div);
}
function yeldAnimal(obj){
var actElement=$(obj).closest('div[ref=dv]');
actElement.yld(); // I want that this yields 'dog'
}
and my HTML:
<input type="button" value="test" onclick="anyFct();">
So this is the logic: I create a div element when the button is clicked on. This div element has a text that when clicked on calls an external function that calls a method on its parent element (the div).
For many contextual reasons this must be the logic. I've already found a solution that is saving the object div in a global array and then search in all values of the array for the object that triggered the method. However, I would like to know if there is a 'cleaner' or correct way to do this.
It's possible, and there are a couple of ways you could achieve it. The important thing you need to understand is the distinction between jQuery objects and actual DOM elements. When you use jQuery to create a <div> element, you create both; but what you end up with a reference to is the jQuery object - or, if you're chaining jQuery function calls, the result of the last function called. The DOM element, assuming you actually append it to the DOM, persists once that section of code has finished execution, but the jQuery object that's created will vanish when that variable goes out of scope.
When you execute some jQuery code later on to get a reference to your DOM element, it's referring to the same element on your page but it's a different jQuery object, so any custom properties you added to the original one won't be available. How do you get around that? Set the properties on the actual DOM element.
You can use the .get() method to access the underlying DOM element from a jQuery object, indexed from 0 (so .get(0) called on a jQuery object will return the first DOM element it references). With that you can then set your custom properties and later retrieve them, something like this:
function anyFct(){
this.div=$('<div ref="dv">').html('Hi').appendTo('body');
var elem = div.get(0); // the actual DOM element, the div
elem.animal='dog';
elem.yld=function(){
alert(elem.animal);
};
$('<input type="text" value="anyIn" onclick="yeldAnimal(this);"/>').appendTo(div);
}
function yeldAnimal(obj){
var actElement=$(obj).closest('div[ref=dv]').get(0); // also the div
actElement.yld(); // alerts 'dog'
}
jsFiddle demo
Note that I've made a few changes to your code in addition to adding in the usage of .get(), most notably correcting the syntax for creating the <input type="text"> element in the first function.
Okay, most of this is not syntactically correct javascript and seems to be overly complicated. I believe if I understand what you're trying to achieve you want the following:
function anyFct(){
var div=$('<div ref="dv">').html('Hi');
div.animal='dog';
div.yld=function(){
alert(this.animal);
};
var element = $('<input type="text" value="anyIn">');
$(element).click(function() {
div.yld();
});
$(div).append(element);
$('body').append(div);
}

[0].click or just .click?

I would like to know why some people use
element.click();
and others use
element[0].click();
What's the difference?
Thanks
Assuming element is a jQuery object, element.click() triggers a click event on a set of HTML elements that the element consists of. It's the same as calling element.trigger("click")
element[0].click() is invoking the click method on a DOM node (not jQuery object) that is the first in the set that element consists of.
See http://api.jquery.com/click/ (first case)
and https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement.click (second case)
for further reference.
They are very different, so they would be used in different contexts.
One calls click on the first element of an array, the other calls click directly on whatever element is

Traversing to a specific child element with Prototype

Given the following markup.
<div id="example">
<div>
<div>
<input type='hidden'></input>
</div>
</div>
</div>
How can I quickly get the hidden input element given I have the ID for the top most div element with the ID 'example'?
I can hack away at it so I can just iterate through each child element until I hit the input, however, I'd like to improve on that and utilize Prototype and simply jump to that hidden input given the div.
Thanks!
Prototype provides a whole bunch of ways to do this:
// This, from Bill's answer, is probably the fastest, since it uses the
// Browser's optimized selector engine to get straight to the element
$$('#example input[type=hidden]').first();
// This isn't bad either. You still use the browser's selector engine
// To get straight to the #example element, then you must traverse a
// (small) DOM tree.
//
// element.down(selector) selects the first node matching the selector which
// is an decendent of element
$('example').down('input');
// Here, you'll get an array containing all the inputs under 'example'. In your HTML
// there is only one.
$('example').select('input')
// You can also use element.select() to combine separate groups of elements,
// For instance, if you needed all the form elements:
$('example').select('input', 'textarea', 'select');
$$('#example input[type=hidden]').first()
I prefer the direct approach
document.forms[0].fieldName.value
Which is less code, no need to use jQuery and is more friendly to code design.

Categories

Resources