jsdom how to get an element inside another element inside foreach - javascript

i am new to JSDOM parser.
I have the following:
<div id='mydiv'>
<ul>
<li><a title='a'>TextA</a></li>
<li><a title='b'>TextB</a></li>
<li><a title='c'>TextC</a></li>
<li><a title='d'>TextD</a></li>
</ul>
</div>
I am using the following code but not able to get the text 'TextA', 'TextB', 'TextC', 'TextD'
const categoryDiv = dom.window.document.querySelectorAll('#mydiv > ul > li')
.forEach(item => {
console.log('item', item.getElement('a')); //not sure how to continue from here
});
})

This could be as simple as:
let targets = document.querySelectorAll('#mydiv > ul > li a');
for (let target of targets) {
console.log(target.text);
}

Simply modify your original code with getElementsByTagName and innerHTML:
const categoryDiv = dom.window.document.querySelectorAll('#mydiv > ul > li')
.forEach(item => {
console.log('item -- ' + item.getElementsByTagName('a')[0].innerHTML);
});
})

const categoryDiv = dom.window.document.querySelectorAll('#mydiv > ul > li')
After this first step you have a NodeList with the 4 list elements. With
console.dir(categoryDiv[0])
you can log the first list object to the console and see and expect all its properties. There are various ways to access the enclosed anchor tags. For example
.children => HTML Collection
.childNodes => NodeList
.getElementsByTagName('a') => HTML Collection
.querySelector('a') => href element
Only the last option gives you the link element directly, with the first three you have to select the first element in the selection to get to the link
For then accessing the text of the link there are again two options
.innerHTML
.textContent
In this case it doesn't matter which to choose because both give you the Text inside the link tags, if called on the link.
If called on the list element it would look like this
listElem.outerHTML // <li><a title="a">TextA</a></li>
listElem.innerHTML // <a title="a">TextA</a>
listElem.textContent // TextA
Sooo you actually don't have to access the link element. Simply call directly .textContent on the list items
Lastly you want to use .map instead of .forEach since .forEach only iterates, but doesn't return anything. The NodeList directly is not iterable with .map but can be easily converted with the spread operator
So all together for example like this
const categoryDiv = [...document.querySelectorAll('#mydiv > ul > li')]
.map(listItem => listItem.textContent)
console.log(categoryDiv) // ['TextA', 'TextB', 'TextC', 'TextD']
or this
const categoryDiv = Array.from(document.querySelectorAll('#mydiv > ul > li'), listItem => listItem.textContent)
Or a very fast way without even iterating would be
document.querySelector('#mydiv ul')
.textContent // 'TextA TextB TextC TextD'
.split(' ') // ['TextA', 'TextB', 'TextC', 'TextD']

Related

How can I select a nested child element of a parent element that has a dynamic ID?

I'm currently toying around with Zendesk, and trying to make changes to a text element on the page. Sadly, Zendesk's elements are dynamic enough that the name of the main element will change on each page load.
Thankfully the structure of the element trees stay pretty static, but it's only the parent element's name that's changing:
#ember6030 > div.comment > div > p
#ember3483 > div.comment > div > p
Currently, here's where I'm at so far:
var item = document.querySelectorAll("[name^=ember] > div.comment > div > p");
var itemtext = item.innerHTML;
console.log(itemtext);
I'm sure I'm missing something, but wouldn't my selector var be correct?
Something like finding an element that begins with "ember" but then follows the rest of the parent-child tree just fine.
EDIT: Things are being a bit more stubborn than I thought, but I've got some extra details if this helps: For the div.comment > div > p elements, a handful of those load up. For right now, I'd like to try targeting just one, but if I can get the text contents of all these elements in console messages, that'd be awesome.
For those CSS paths you would use:
var item = document.querySelector("[id^=ember] > div.comment > div > p");
var itemtext = item.textContent;
console.log(itemtext);
since # is the CSS selector for the id attribute, not the name.
See also David Klinge's answer about NodeLists.
So the final code is:
var items = document.querySelectorAll("[id^=ember] > div.comment > div > p");
items.forEach (item => {
var itemtext = item.textContent;
console.log(itemtext);
} );
Finally, for what you seem to be trying to do, you probably want textContent instead of innerHTML. This avoids false hits on attributes, comments, etc.
document.querySelectorAll returns a NodeList that must be iterated over. You should be able to use a forEach to iterate over the elements querySelectorAll selects.
var items = document.querySelectorAll("[id^=ember] > div.comment > div > p");
items.forEach (item => {
var itemtext = item.textContent;
console.log(itemtext);
} );

how to get the index of the li clicked in javascript

Just using javascript, need to get the index of the li that's clicked with the following listener:
var ulList = document.getElementById('todo-list');
ulList.addEventListener('click', function(e){
if( e.target.nodeName == "BUTTON") {
//IS THERE A WAY INSIDE HERE TO GET THE INDEX OF THE li clicked
e.target.parentNode.remove();
}
});
each li looks like:
<li>
<input type="checkbox" value="1" name="todo[]">
<span class="centerSpan" style="text-decoration: none;">abc</span>
<button class="rightButton">X</button>
</li>
From the target (button, in this case) call closest() to get a reference to your li element
var li = e.target.closest('li');
Then get an array reference of your UL's children by using Array.from() and passing in the children HTMLCollection
var nodes = Array.from( ulList.children );
//or if you want to not depend on externally defined variables
var nodes = Array.from( li.closest('ul').children );
Finally call indexOf() to get the index
var index = nodes.indexOf( li );
If wanting to support old browsers will need to polyfill for methods like Array.from()

How to Get visible element by class?

I have no option but javascript. I am sure lots of people went through this.
HTML code
<ul class="recordingslist"></ul>
..
..
..
<div class='test' style="display : none">
<ul class="recordingslist test1" ></ul>
</div>
..
..
..
<div class="testasdasdsad" style="display : none">
<ul class="recordingslist test2"></ul>
</div>
JS code
recordingslist = document.getElementsByClassName("recordingslist")[0];
I have many ul with the same class. Now only first ul is visible, I want to append "test" to that visible ul. How can we achieve this?
Thank you very much in advance.
Based on your clarification in the comments, you're searching for the first <ul> element whose parent <div> element has a display property which is not equal to none.
Given that, I'd suggest:
// here we use Array.from() to convert the supplied Array-like
// NodeList into an Array, in order to use Array methods:
Array.from(
// here we find all <ul> elements with a class of
// 'recordingslist':
document.querySelectorAll('ul.recordingslist')
// we filter the resulting Array of <ul> elements, using
// Array.prototype.filter():
).filter(
// using an Arrow function, in which the 'ul' variable
// is a reference to the current <ul> element of the Array
// of <ul> elements over which we're iterating:
ul => window.getComputedStyle(
// we find the computed CSS properties of the node:
ul.parentNode, null
// and access its 'display' property to ensure that
// it's computed display property-value is not equal
// to the value of 'none' (so it should not be hidden):
).display !== 'none'
// we iterate over those elements retained in the Array,
// using Array.prototype.forEach
).forEach(
// here we use another Arrow function:
// ul: the current <ul> in the Array of <ul> elements,
// index: the index of the current <ul> in the Array
// of <ul> elements.
// here if the index is exactly equal to 0, we add the
// 'test' class-name, otherwise we add an empty string:
(ul, index) => ul.classList.add( index === 0 ? 'test' : '' )
);
References:
Array.from().
Array.prototype.filter().
Array.prototype.forEach().
Arrow Functions.
Conditional, ternary, operator (assessment ? ifTrue : ifFalse).
Element.classList API.
Using jquery makes this quite a breeze. Suggest adding an additional attribute to your ul divs to mark if the div should be visible or not. Something like below
HTML
<ul visible=false class="recordingslist"></ul>
<ul visible=false class="recordingslist test1"></ul>
<ul visible=true class="recordingslist test2"></ul>
JS
let allElems = document.querySelectorAll('.recordingslist')
allElems.forEach((obj)=>{
if(obj.getAttribute('visible')==='true'){
obj.append('Hello there')
}
})
Use the jquery selector :visible for example, in your case, if you want to select only ul visibles:
$('ul:visible').append('test');
or using the class
$('.recordingslist:visible').append('test');
official docs: https://api.jquery.com/visible-selector/

Refreshing the order of an ordered list, after appending a list item

jsFiddle example
I'm attempting to use an ol to display several, dynamic, list items. Upon appending a list item using jQuery, I'd have hoped that the browser would've refreshed the ordered list, however it doesn't appear to be.
<ol>
<li value="1">First</li>
<li value="2">Second</li>
<li value="4">Forth</li>
</ol>
After appending <li value="3">Third</li>, the resulting order is:
// First
// Second
// Forth
// Third <-
Do I need to trigger a re-order, or is the order only calculated upon rendering the page / adding the list DOM element?
Note: I'm using Chrome 28.0.1500.95m
Firstly, note that value is not a valid attribute of li, so your code is invalid. You should use a data attribute for any non-standard attributes you require, the you need to write a sorter function. Try this:
$('#addThird').one('click', function() {
$('ol').append('<li data-value="3">Third</li>');
sortItems();
});
var sortItems = function() {
var $items = $('ol li');
$items.sort(function(a, b) {
var keyA = $(a).data('value');
var keyB = $(b).data('value');
return (keyA > keyB) ? 1 : 0;
});
$.each($items, function(index, row){
$('ol').append(row);
});
}
Updated fiddle
This sorts by the data-value attribute of the element, but that can be amended as required.

get text of the first li element from every ul

I have a lot of <ul> list and try to get from every first li of this list the text.
the markup is simple like:
<ul>
<li>abc</li>
<li>def</li>
<li>ghi</li>
</ul>
and so on.
my jQuery attempt is:
var elems = $('ul'); // returns a nodeList
var arr = jQuery.makeArray(elems);
arr.reverse(); // use an Array method on list of dom elements
for( var i=0; i < elems.length; i++) {
console.log($(this).find('li:lt(1)').text());
}
But I have a mistake in the for loop with $(this). I don't know how to get the first text of ul number 1 or 3 if i don't use $(this).
So how can point it correctly in the for loop?
.each will give you this.
$('ul').each(function() {
console.log($(this).find('li').eq(0).text());
})
Alternative sytax using :first instead of :eq(0)
$('ul').each(function() {
console.log($(this).find('li:first').text());
});
or, to forgo the find() function.
$('ul').each(function() {
console.log( $('li:first', this).text() );
});
you can also use:
$("li:nth-child(1)").each(function()
{
console.log($(this).text());
});
notes:
with :nth-child(n), all children are counted, regardless of what they are.
with :nth-child(n), n is 1-based (the first index is 1 instead of 0)

Categories