How to Get visible element by class? - javascript

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/

Related

Get last class from div with javascript

Given a div such as <div class="abcd ws-compid rh03 a11y-true"></div>, I need to be able to find the last class on each div and pull it's value.
I can currently get all the classes with classList, but can't seem to get the last class in the DOMTokenList:
[...document.querySelectorAll('.ws-compid')].map(component => component.classList ? {id: component.innerText, type: component.classList} : null);
The class I need will always be the last one in the array, but options like .last() don't seem to work.
You can also work with .classList and turn it into an array:
document.querySelectorAll(".ws-compid").forEach(d=>console.log([...d.classList].at(-1)))
<div class="abcd ws-compid rh03 a11y-true">one</div>
<div class="abcd ws-compid rh03 a12y-false">two</div>
Array.from(
document.getElementsByTagName("div")
).map( i => ({
type: i.className.split(' ').pop(),
id: i.innerText
}))
create an array from all div Elements, them map to their last className (type) and value (id)
consider filtering those empty (not all tag have innerText or classes)
Figured out the solution, since it's actually an object, I needed to use Object.entries():
[...document.querySelectorAll('.ws-compid')].map(component => component.classList ? {id: component.innerText, type: Object.entries(component.classList).pop()[1]} : null);

jsdom how to get an element inside another element inside foreach

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']

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.

Why does jQuery, outputs same rel each time?

so basically here is my script:
http://jsfiddle.net/JJFap/42/
Code -
jQuery(document).ready(function() {
var rel = new Array();
var count = 0;
jQuery(".setting").each(function() {
rel[count] = [];
if(jQuery("span").attr("rel")) {
rel[count].push(jQuery("span").attr("rel"));
}
console.log(count);
count++;
});
jQuery("body").text(rel);
console.log(rel);
});​
and
<div class="setting">
<span rel="Variable">Variable</span>
<span rel="Item">Item</span>
<span rel="Something">Something</span>
</div>
<div>
<span rel="Smth">Smth</span>
<span>Sec</span>
</div>
<div class="setting">
<span>Second</span>
<span rel="first">First</span>
<span rel="Third">Third</span>
</div>
​my question, is why does it display Variable, variable?
I would like it to display Variable, First, but I'm not able to do.
Basically what I would like to achieve is create new array, in which insert each div.setting span elements with rel attribute array.
So basically in this example it should output -
Array (
Array[0] => "Variable","Item","Something";
Array[1] => "first","Third";
)
Hope you understood what I meant :)
EDIT:
In my other example I tried to add jQuery("span").each(function() ... inside first each function, but it outputted two full arrays of all span elements with rel. I can't have different classes / ids for each div element, since all will have same class.
jQuery('span') is going to find ALL spans in your page, and then pull out the rel attribute of the first one. Since you don't provide a context for that span search, you'll always get the same #1 span in the document.
You should be using this:
jQuery('span',this).each(function() {
rel[count] = [];
if (jQuery(this).attr("rel")) {
rel[count].push(jQuery(this).attr("rel"));
}
console.log(count);
count++;
})
instead of this:
rel[count] = [];
if(jQuery("span").attr("rel")) {
rel[count].push(jQuery("span").attr("rel"));
}
console.log(count);
count++;
http://jsfiddle.net/mblase75/JJFap/52/
The trick is to use a second .each to loop over all the span tags inside each <div class="setting"> -- your original code was using jQuery("span"), which would just grab the first span tag in the document every time.
In addition to what has been said, you can also get rid of the count and one push() when using jQuery.fn.map() as well as getting rid of the if when adding [rel] to the selector:
jQuery(document).ready(function() {
var rel = [];
jQuery(".setting").each(function() {
rel.push(jQuery(this).find('span[rel]').map(function() {
return this.getAttribute('rel');
}).get());
});
jQuery("body").text(rel);
console.log(rel);
});
Within the .each() method, you have this code a couple times: jQuery("span").attr("rel"). That code simply looks for ALL span tags on the page. When you stick it inside the .push() method, it's just going to push the value for the rel attribute of the first jQuery object in the collection. Instead, you want to do something like $(this).find('span'). This will cause it to look for any span tags that are descendants of the current .setting element that the .each() method is iterating over.

jQuery .text() length behavior

I have my HTML coded as follows;
<div class="welcomeText">
<ul>
<li><span<%=Some Java Code%></span>
</li>
</ul>
</div>
Also there is a Javascript code called after document is ready which has the following line;
var welcomeLen = $(".welcomeText span").text().length;
Now if I want to update my HTML code inside li as follows;
<li><span><span class="firstNameWithEllipses"><%=Some Java Code%></span></span>
i.e. I want to add a new span element with class="firstNameWithEllipses"
The issue that I am facing is that the JS calculation for welcomeLen changes if I add the above HTML code.
I am not quite sure how the text().length works as it returns the following values for the 2 separate cases;
When rendered as
<span>Hello, StudentFname87654 from Functional!</span>
it returns 41
&
When rendered as
<span>Hello, <span class="firstNameWithEllipses">StudentFname87654</span> from Functional!</span>
it returns 58
How do I ensure that the welcomeLen remains the same even if I add any HTML code inside the span ?
This is because your selector matches both span elements and returns the text from both. You could have added any element other than a child span and you would not have experienced this issue:
console.log($(".welcomeText span").text());
//Output: Hello, StudentFname87654 from Functional!StudentFname87654
Make your selector more specific. You could, for example, select only span elements that are direct children of an li that is a descendant of .welcomeText:
var welcomeLen = $(".welcomeText li > span").text().length;
Here's a working example.
Update
If you're interested in why this is the case, you can look at the jQuery source for the text method. Like most jQuery methods, it iterates over all of the elements in the matched set:
var ret = "";
jQuery.each( text || this, function(){ //Iterate over matched set
jQuery.each( this.childNodes, function(){ //Iterate over children of current element
if ( this.nodeType != 8 )
ret += this.nodeType != 1 ?
this.nodeValue : //Append text to overall string
jQuery.fn.text( [ this ] ); //Get text of descendants
});
});
return ret;
var welcomeLen = $(".welcomeText span").not(".firstNameWithEllipses").text().length;

Categories