jQuery iterate through elements within another element - javascript

I have HTML like this:
<div class="foo">
<div class="bar1">A</div>
<div class="bar2">B</div>
<div class="bar3">C</div>
</div>
<div class="foo">
<div class="bar1">D</div>
<div class="bar2">E</div>
<div class="bar3">F</div>
</div>
<div class="foo">
...etc.
I am trying to iterate through the "foo" divs to create objects like {bar1: A, bar2: B, bar3: C} with code sort of like this:
var arrayOfObjects= [];
var rows = $(".foo");
for (var i=0; i<rows.length; i++) {
var row = rows[i];
arrayOfObjects.push(
{
bar1: row.find(".bar1").text(),
bar2: row.find(".bar2").text(),
bar3: row.find(".bar3").text()
}
);
}
I understand that this doesn't work because the original var rows = $(".foo"); creates an array of DOM elements, which don't have find() as a function. I also know that within the loop, I could start using elementByClass and innerHtml, but I feel like my brain starts crying whenever I start mixing jQuery-style and DOM-style selectors in the same code.
Is there a way to fix my code above so that I'm using jQuery selectors within the loop?

You can wrap your elements as jQuery objectslike this:
arrayOfObjects.push(
{
bar1: $(row).find(".bar1").text(),
bar2: $(row).find(".bar2").text(),
bar3: $(row).find(".bar3").text()
}
);
This makes you row a JQuery object, which has the 'find' method.

You can easily iterate through .row divs by using each(),
var arrayOfObjects= [];
$(".foo").each(function(){
var items = {"bar1" : $(this).find('.bar1').text(),"bar2" : $(this).find('.bar2').text(), "bar3" : $(this).find('.bar3').text()};
arrayOfObjects.push(items); //If you want to push all into an object and then into an array
//or to use it on its own
$(this).find('.bar1').text();
$(this).find('.bar2').text();
$(this).find('.bar3').text();
});
Hope this helps.

//find all the foo, and map them into new elements
var result = $('.foo').map(function(index, element){
//we want to map all the children of the element into a single object
return $(element).children().get().reduce(function(aggregate, childElement){
//get the class off of the child and it's value, put them in the object
aggregate[childElement.className] = childElement.innerText;
return aggregate;
}, {}); //second argument to the reduce() is the starting element
}).get(); //use get() to break the array out of the jQuery object
console.log(result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="foo">
<div class="bar1">A</div>
<div class="bar2">B</div>
<div class="bar3">C</div>
</div>
<div class="foo">
<div class="bar1">D</div>
<div class="bar2">E</div>
<div class="bar3">F</div>
</div>

$(document).ready(() => {
var arrayOfObjects = $('.foo').map(function() {
return $(this).find('>*').map(function(obj) {
return {
class: $(this).attr('class'),
text: $(this).text()
};
}).get().reduce( (obj, arr) => {
obj[arr.class] = arr.text;
return obj;
}, {});
}).get();
console.log(arrayOfObjects);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="foo">
<div class="bar1">A</div>
<div class="bar2">B</div>
<div class="bar3">C</div>
</div>
<div class="foo">
<div class="bar1">D</div>
<div class="bar2">E</div>
<div class="bar3">F</div>
</div>
hope this helps you :)

Something along these lines with .each would probably work
const $rows = $('.foo');
let arrayOfObjects = [];
$rows.each(function(i) {
const $row = $(this);
let obj = {};
$row.children().each(function(ch) {
obj = { ...obj, [this.className]: $(this).text() };
});
arrayOfObjects = [ ...arrayOfObjects, obj ];
});
console.log(arrayOfObjects);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="foo">
<div class="bar1">A</div>
<div class="bar2">B</div>
<div class="bar3">C</div>
</div>
<div class="foo">
<div class="bar1">D</div>
<div class="bar2">E</div>
<div class="bar3">F</div>
</div>

Related

How to addEventListener and function with element as a parameter to multiple elements?

My goal is to have a bunch of div with clickable words to pass their ids to a Javascript function when one of them is clicked by the user. It works flawlessly for
<div id="wordbox">
<div id="pd"><h4>pdf</h4><br></div>
<div id="an"><h3>analysis</h3></div>
<div id="ai"><h2>artificial intelligence</h2></div>
<div id="tr"><h4>trends</h4><br><br></div>
<div id="dm"><h3>data mining</a></div>
</div>
var word = document.getElementById("pd");
word.addEventListener("click", function(){ showSlide(word.id) });
but I don't manage to get it working for all elements. This fails:
var wb = document.getElementById("wordbox").children;
wb.forEach(function (element, index){
element.addEventListener("click", function(){
showSlide(element.id);
});
});
Any ideas?
When you select the children from a node, it actually returns an array-like collection which is similar but not quite an array. In order to use forEach you first need to convert it into an array, and in the case below, I used the spread syntax to convert it into an array that allows me to use forEach.
const wb = document.querySelector('#wordbox')
const children = [...wb.children]
children.forEach(child => {
child.addEventListener('click', () => {
console.log(child.id)
})
})
<div id="wordbox">
<div id="pd"><h4>pdf</h4><br></div>
<div id="an"><h3>analysis</h3></div>
<div id="ai"><h2>artificial intelligence</h2></div>
<div id="tr"><h4>trends</h4><br><br></div>
<div id="dm"><h3>data mining</a></div>
</div>
wb is not a real array, it is an array-like object called live HTMLCollection. You can get an array using Array.from(). You can also use document.querySelectorAll() to select the elements
var wb = document.querySelectorAll("#wordbox > div");
// new browsers - https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach#bcd:api.NodeList.forEach
// wb
// To support older browsers
Array.from(wb)
.forEach(function(element, index) {
element.addEventListener("click", function() {
console.log(element.id);
});
});
<div id="wordbox">
<div id="pd">
<h4>pdf</h4><br></div>
<div id="an">
<h3>analysis</h3>
</div>
<div id="ai">
<h2>artificial intelligence</h2>
</div>
<div id="tr">
<h4>trends</h4><br><br></div>
<div id="dm">
<h3>data mining</a>
</div>
</div>

Including the current node in the find scope

Consider the following snippet as an example:
<div class="bar foo">
</div>
<div class="bar">
<div class="foo"></div>
</div>
Given var $set=$('.bar'); I need to select both nodes with foo class. What is the proper way to achieve this. Considering addBack() requires a selector and here we need to use the $set jQuery object and $set.find('.foo') does not select the first node.
use this :
var $set = $(".bar").filters(function () {
var $this = $(this);
if($this.is(".foo") || $this.find(" > .foo").length !== 0){
return true;
} else{
return false;
}
});
Here's one way of going about it:
var set = $('.bar');
var foos = [];
for (var i = 0; i < set.length; i++) {
if ($(set[i]).hasClass('foo')) {
foos.push(set[i]);
}
}
if (set.find('.foo').length !== 0) {
foos.push(set.find('.foo')[0]);
}
console.log(foos);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="bar foo"></div>
<div class="bar">
<div class="foo"></div>
</div>
The for loop checks all elements picked up with jQuery's $('.bar'), and checks if they also have the foo class. If so, it appends them to the array. The if checks if any of the elements picked up in set have any children that have the foo class, and also adds them.
This creates an array that contains both of the DIVs with the foo class, while excluding the one with just bar.
Hope this helps :)
test this :
var $newSet = $set.filter(".foo").add($set.has(".foo"));
You could use the addBack() function
var $set=$('.bar');
console.log($set.find(".foo").addBack(".foo"));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="bar foo">
</div>
<div class="bar">
<div class="foo"></div>
</div>

How to sort a list of nodes by a given array in an efficient way?

I have this HTML which is a list of elements:
<div class="container">
<div class="apple-0">first-apple</div>
<div class="apple-1">second-apple</div>
<div class="apple-2">third-apple</div>
<div class="apple-3">forth-apple</div>
<div class="apple-4">fifth-apple</div>
</div>
I've gotten an array, for example, which is [3,4,0,2,1] I need to sort the list in to this order.By this I mean that the third element <div class="apple-3">third-apple</div> should be the first. The second element should be the forth-apple.
How can I change it in an efficient way? This is the expected output:
<div class="container">
<div class="apple-3">forth-apple</div>
<div class="apple-4">fifth-apple</div>
<div class="apple-0">first-apple</div>
<div class="apple-2">third-apple</div>
<div class="apple-1">second-apple</div>
</div>
jQuery can be used.
You can do this by looping through the array and appending each div by it's matched index. Try this:
var $divs = $('.container > div').detach();
[3, 4, 0, 2, 1].forEach(function(value) {
$divs.eq(value).appendTo('.container');
});
Working example
Note that if you need to support older browsers (< IE9) then you would need to replace forEach() with a standard for loop.
You can try something like this:
$("#sort").on("click", function() {
var data = [3, 4, 0, 2, 1];
var result = "";
data.forEach(function(item) {
result += $(".container").find(".apple-" + item)[0].outerHTML;
});
$(".container").html(result);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<div class="container">
<div class="apple-0">first-apple</div>
<div class="apple-1">second-apple</div>
<div class="apple-2">third-apple</div>
<div class="apple-3">forth-apple</div>
<div class="apple-4">fifth-apple</div>
</div>
<button id="sort">Sort</button>
Simply iterate the indexes array and keep pushing the child at nth-index
var output = [];
var indexes = [3,4,0,2,1];
indexes.forEach(function(value, index){
output.push($(".container div").eq(indexes[index])[0].outerHTML);
});
console.log(output);
$(".container").html(output.join(""));
Demo
you can try:
UPDATE:
var arr = [3,4,0,2,1];
var nodes = [];
arr.forEach(funtion(value){
var node = $('.container .apple-'+value)[0];
nodes.push(node);
});
$('.container').html(nodes);
demo
Other answers with eq are good, but if you want to sort again with a different array, or the array is unsorted initially, then they would fail. Also you asked for an efficient method, using native loops instead of jquery's each gives performance benefits. So my answer to this is
$(document).ready(function () {
var inputEls = $('#awesomeContainer').find('>').get(),
$output = $('#awesomeOutput'),
order = [3,4,0,2,1],
output = [],
myValue,
newIndex,
i,
length = inputEls.length;
for (i = 0; i < length; i += 1) {
myValue = Number((inputEls[i].className || "").replace("apple-", ""));
if (myValue >= 0) {
myValue = order.indexOf(myValue);
myValue > -1 && (output[myValue] = inputEls[i].outerHTML);
}
}
$output.append(output.join(''));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<b>Input: </b>
<div id="awesomeContainer" class="container">
<div class="apple-0">first-apple</div>
<div class="apple-1">second-apple</div>
<div class="apple-2">third-apple</div>
<div class="apple-3">forth-apple</div>
<div class="apple-4">fifth-apple</div>
</div>
<br/>
<b>Sorted: </b>
<div id="awesomeOutput" class="container">
</div>

How to iterate through all div and get specific property?

what I'm trying to do is iterate over a collection of div, contained in a parent container. My structure is the following:
<div id='main'>
<div data-id='2'>
</div>
<div data-id='3'>
</div>
</div>
My goal is take the field data-id of each div and create an array collection. Previously I used the select where do I get each value of available option, like this:
var available_services = $('#selected-service').find('option', this).map(function ()
{
return this.value;
}).get();
But now I'm using a div collection instead of the select. How I can iterate through all available div?
This should return all data-id values in a list:
var available_services = $('#main').find('div').map(function (item)
{
return item.attr('data-id');
});
I didn't test this, but I think should do the job. (maybe you need to tweak a little bit)
I believe this will do it:
var available_services = [];
$('#main div').each(function(){
available_services.push($(this).data( "id" ));
})
This is the easy way to go:
$(document).ready(function() {
var myCollection = [];
$('#main div').each(function(){
var dataDiv = $(this).attr('data-id');
myCollection.push(dataDiv)
})
});
Try this:
(function(){
var main = $("#main");
var divs = $(main).find("div");
var arrId = divs.map(function(index, div){
return $(div).attr("data-id");
});
console.log(arrId);
})()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id='main'>
<div data-id='2'>
</div>
<div data-id='3'>
</div>
</div>

Loop in jquery based on razor loop

I have a for each loop in my razor view (MVC4)
#foreach (var fe in ViewBag.FeatureProducts)
{
<div class="product-details" style="display:none;">#Html.Raw(fe.Details)</div>
<div class="product-qty-dt"> </div>
}
in the each loop i have to extract the #html row content and display in to the div 'product-qty-dt'.
For that I write the following jquery,
$(document).ready(function () {
var data = $('.product-details p').map(function () {
return $(this).text();
}).get();
//product availability
var availability = data[2].split(",");
$.each(availability, function (i) {
$('.product-qty-dt').append( availability[i])
});
});
But this was only consider the last foreach raw. how can i call this jquery in each loop call.
For example, In first loop,
<div class="product-details" style="display:none;"><p>Lorem, Ispum</p><p>doler emit,fghjh</p><p>9gm</p></div>
<div class="product-qty-dt"> 9gm </div>
second loop
<div class="product-details" style="display:none;"><p>Lorem, Ispum</p><p>doler emit,fghjh</p><p>5gm</p></div>
<div class="product-qty-dt"> 5gm </div>
Thirdloop
<div class="product-details" style="display:none;"><p>Lorem, Ispum</p><p>doler emit,fghjh</p><p>3gm</p></div>
<div class="product-qty-dt"> 3gm </div>
The following will add the text of the last <p> in a <div class="product-details"> element into the corresponding <div class="product-qty-dt"> element
$('.product-details').each(function () {
var t = $(this).children('p').last().text();
$(this).next('.product-qty-dt').text(t);
})
This should do the trick. Also the data array is still used just incase you would like to access other data portions stored in product-details.
$(".product-details").each(function()
{
var data = $(this).find('p').map(function () { return $(this).text() }).get();
$(this).next('.product-qty-dt').text(data[2]);
})

Categories