JQuery: Loop through elements and set as variable for outside scope - javascript

I am trying to retrieve a DOM element from an array, and I want to set it as a variable to use outside its scope. Right now, my variable future_devices returns one object as expected. But my other variable future_device returns the object when the current DOM should have returned [] due to my last if statement. I originally tried to declare my variables as var due to scope but that did not help. Here is my code:
var future_devices = $('.hardware .future-hardware')
if (future_devices.length) {
let future_device = $(future_devices)
.each(function() {
let device = this
let device_work_order = $(device)
.data(
'work-order'
)
if (device_work_order == data['new_host']['work_order']) {
return device
}
})
I can tell you on the said DOM, the two variables I am using to compare have the following values:
device_work_order = 3MOD0
data['new_host']['work_order'] = 3MOD9
So since future_devices returns only one object and my last if statement is not true, I should get [], right?

$(...) is returning the jQuery collection and always will regardless. So an assignment statement using .each() is the wrong approach.
Solution: Assign the return of .filter() instead. Filter is designed to accomplish your goal. Reference
NOTE: You should realize that if there is more than one match, it will return the entire collection of matches. In the code below I show only the first match, but since there are two matches (for demonstration), you'll see that both matches are returned.
const future_devices = $('.hardware .future-hardware');
const data = {new_host: {work_order: 333}};
const future_device = $(future_devices)
.filter(function(idx, el) {
let device_work_order = $(el).data('work-order');
if (device_work_order == data['new_host']['work_order']) {
return true;
} else {
return false;
}
})
console.log("First match only: ", future_device[0]); // First match
console.log("Collection: ",future_device); // All matches
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="hardware">
<div class="future-hardware" data-work-order="111">111</div>
</div>
<div class="hardware">
<div class="future-hardware" data-work-order="333">First Match</div>
</div>
<div class="hardware">
<div class="future-hardware" data-work-order="111">111</div>
</div>
<div class="hardware">
<div class="future-hardware" data-work-order="333">Second Match</div>
</div>
<div class="hardware">
<div class="future-hardware" data-work-order="111">111</div>
</div>
<div class="hardware">
<div class="future-hardware" data-work-order="111">111</div>
</div>

Related

how to find out each index of a div

For example, I have
<div class="stick active"></div>
<div class="stick"></div>
<div class="stick"></div>
<div class="stick"></div>
<div class="stick"></div>
I need to find out all the indexes from the stick classes so that I can refer to each of them further [0],[1],[2]...
I tried to convert them to an array via [...] and via Array.prototype.slice.call
but when I try to interact with them, I get "undefined"
use this I think it will help you:
for first index:
var x = document.querySelectorAll(".stick");
console.log(x[0])
for all:
var x = document.querySelectorAll(".stick");
console.log(x)
Use Document.querySelectorAll() to create a NodeList.
querySelectorAll():
https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll
NodeList:
https://developer.mozilla.org/en-US/docs/Web/API/NodeList
const highlightedItems = document.querySelectorAll(".stick");
// access the second node:
console.log(highlightedItems[2].textContent);
// loop through all nodes
for (let i = 0; i < highlightedItems.length; i++) {
let item = highlightedItems[i];
console.log( item.id );
}
<div class="stick active"></div>
<div class="stick" id="1">A</div>
<div class="stick" id="2">B</div>
<div class="stick" id="3">C</div>
<div class="stick" id="4">D</div>
I think you want to hanle it via getElementsByClassName. This returns an HTML collection, not an array, thus you cannot use indexOf on it.
Iterating over the elements is the way to go, iterating over className, which is a string containing your node's class name, like this:
let stick = document.getElementsByClassName('stick');
for(let i=0; i < stick.length; i++){
console.log(stick[i]);
}

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>

Compare order of two HTML elements

I have a function which accepts two parameters, each of type HTML element. It is supposed to return which element appears first in the document order. Is there any simple way to determine this?
Template -
<body>
<div id="div1">
<div id="div2">
</div>
</div>
<div id="div3">
<div id="div4">
</div>
</div>
</body>
JS -
const elem1 = document.getElementById('div2');
const elem2 = document.getElementById('div4');
const firstAppearingElement = checkOrder(elem1, elem2); // it should return elem1
function checkOrder(element1, element2) {
// check which one appears first in dom tree
}
You can try with Node.compareDocumentPosition()
The Node.compareDocumentPosition() method compares the position of the
given node against another node in any document.
The syntax is object.compareDocumentPosition (nodeToCompare);
let first = document.getElementById('a');
let second=document.getElementById('b');
// Because the result returned by compareDocumentPosition() is a bitmask, the bitwise AND operator has to be used for meaningful results.See link above for more
if (first.compareDocumentPosition(second) & Node.DOCUMENT_POSITION_FOLLOWING) {
console.log('element with id a is before element with id b'); //
} else {
console.log('element with id a is after element with id b');
}
<div id="a"></div>
<div id="b"></div>

JS Remove all elements except a specific ID and its children

Responses to this:
How to remove elements except any specific id
are close to what I want but not quite.
In my case I am asking how I can remove all elements under parent id except id_n and its children: test1 and test2. The elements need to be removed, not just hidden.
<div id = "parent_id">
<div id = "id_1">
<div id = "id_11"> test</div>
<div id = "id_12">test </div>
</div>
<div id = "id_2"> test</div>
<div id = "id_n">id_n<br>
<div id='test1'>test1<br><div>
<div id='test2'>test2<br><div>
</div>
</div>
The result should be:
<div id = "parent_id">
<div id = "id_n">id_n<br>
<div id='test1'>test1<br><div>
<div id='test2'>test2<br><div>
</div>
</div>
Thanks for looking at this. Your suggestions are appreciated.
Using jQuery's siblings you remove all of it's children:
$('#id_n').siblings().remove();
Okay after thinking about this, there is another approach using Array manipulation:
var parentElement = document.getElementById('#parent_id');
parentElement.innerHtml = [].splice.call(parentElement.children).filter(item, function() {
return item.id === childId;
}).reduce((collatedHtml, item, function() {
return collatedHtml + item.innerHtml;
});
This grabs all the direct children of the parentElement and returns a new array (using Array.filter) before using Array.Reduce to collate the innerHtml of all the children.
Note: the reason i'm not using the ... prefix to convert to an Array is because it is not supported in IE 11 and below

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>

Categories