How to take id value and add it grandparent as an attribute - javascript

Hi everyone first post here and I'm working on a navigation bar and needed to add and id and value to the ul, then in an anchor tag above it, add an attribute of aria-controls = the value of the id I made for the ul.
<li class="hs-menu-item hs-menu-depth-1 hs-item-has-children" role="menu">
Rates
<a class="child-trigger"><span></span></a>
<ul class="hs-menu-children-wrapper">
So far I've added the id and added the attribute with Javascript.
document.getElementsByClassName("hs-menu-children-wrapper")[0].setAttribute("id","Rates");
document.getElementsByTagName("a")[10].setAttribute("aria-controls","Rates");
which gave me what I wanted.
Rates
<a class="child-trigger"><span></span></a>
<ul class="hs-menu-children-wrapper" id="Rates" >
but I have 5 other elements with id's that I need to apply this to and targeting the different anchor tags won't work for every page we plan to use this with. I pretty much need help simplifying this thanks in advance.

So, assuming each is contained in its own parent element, something like this could work. Of course, you'd want to give the trigger a some sort of class to identify and hook into.
/**
* First, we create a list of all the uls we want to connect.
* Note that, because some browsers don't treat a list of
* nodes as an Array, we want to convert it to an array.
* Here, I'm using the ES6 spread operator, which may not be
* the best option, but for demonstration purposes, it works.
**/
const ulEls = [...document.querySelectorAll("ul")];
/**
* Now, iterate over that array. With each ul, we want to find
* a parent element, and then within that, locate the anchor
* that will be made the control for this ul. Once we find
* that, we can assign it the attribute with the value we want.
**/
ulEls.forEach( (ulEl) => {
let container = ulEl.parentNode,
triggerEl = container.querySelector(".ul-controller");
triggerEl.setAttribute("aria-controls", ulEl.id);
} );
<div>
<a class='ul-controller' href="https://www.website.org" aria-expanded="false">Rates</a>
<a class="child-trigger"><span></span></a>
<ul class="hs-menu-children-wrapper" id="Rates" >
...
</ul>
</div>
<div>
<a class='ul-controller' href="https://www.website.org" aria-expanded="false">Sizes</a>
<a class="child-trigger"><span></span></a>
<ul class="hs-menu-children-wrapper" id="Sizes" >
...
</ul>
</div>
<div>
<a class='ul-controller' href="https://www.website.org" aria-expanded="false">Times</a>
<a class="child-trigger"><span></span></a>
<ul class="hs-menu-children-wrapper" id="Cheezburger" >
...
</ul>
</div>
If you open the console, you'll see that the aria-controls is being set on each dynamically. Basically, it iterates over each ul, and uses that as the starting point for setting its sibling a to the appropriate aria-controls using setAttribute().
EDIT: As was pointed out, querySelectorAll returns a nodeList, which not every browser will treat as an Array. So it is best practice to convert that into an array before trying to run forEach over the set.

Related

Obtaining Upper level li text

So I currently have a list like so on my page
<li class="head">
<b>Introduction</b>
<ul>
<li class="sub">somethingsomething</li>
</ul>
</li>
This list is being used with sortable, so the user can decide on the order, and I am passing this information to a grails controller for use in application logic. So, I am trying to read it in, and place the text contained in the "head" and "sub" classes in 2 different arrays. However, when I use a jquery selector to obtain the head elements, and obtain the text attribute of the element, it contains the inside list as well.
$('#divname').find("ul > li.head").each(function()
{
var current = $(this);
console.log(current.text());
});
results in Introductionsomethingsomething
Is there any way to only obtain the 'Introduction' text from the list, and ignore the text in the nested <ul> and <li.sub>? Due to it being nested, I am unable to figure out how to use jQuery's :not() selector
You can find the b tag using jquery tagname selector.Like this:
var current = $(this).find('b');
console.log(current.text());
Working Demo
May be this is solution:
<script>
$('#divname').find("ul > li.head").each(function()
{
var current = $(this).find("b");
console.log(current.text());
});
</script>

Custom attribute Not fetchable from li

I have following Code/Structure, what I am trying to do is to hide a div if a custom attribute matches. The problem at the moment is that I can't get the custom attribute as demonstrated in this code:
var elementToHide = 'file_type';
jQuery('#search-img-ctrl').each(function() {
var locationli = jQuery(this).find('li').attr(elementToHide);
alert(locationli);
alert(elementToHide); // I can't get the custom attribute
if (locationli != elementToHide) {
jQuery(this).find('.search-img-box').hide();
} else {
jQuery(this).find('.search-img-box').show();
}
});
And following is my HTML Structure.
<div id="search-img-ctrl" class="search-img-ctrl">
<div class="sampages" style="display: block;">
<div class="search-img-box sampageitems">
<a href="image_detail.php">
<img id="imageimage_array" width="277" height="206" src="upload/2014-05-02-14-05-512014-04-08-14-04-40000560_d.png" alt="">
</a>
<br>
<ul>
<li> Name </li>
<li>upload/2014-05-02-14-05-512014-04-08-14-04-40000560_d.png</li>
<li>identity </li>
<li>Modify</li>
<li latitude="null">Latitude</li>
<li>null</li>
<li longitude="null">Longitude</li>
<li>null</li>
<li model="null">model</li>
<li>null</li>
<li file_type="png">model</li>
<li>png</li>
<li> Image Size </li>
<li>11Kb</li>
</ul>
</div>
</div>
Ideally under html5 you should suffix your custom attributes with data- prefix. However, in your code to find the li that has specific attribute, use:
var locationli = jQuery(this).find('li[' + elementToHide + ']');
Here is a JSFiddle demonstrating this: http://jsfiddle.net/wANxV/
The main wrapper have id and class same value. This is not a good.
Put a numer or other after your id value (id="search-img-ctrl-1" etc) , then do each cycle on class not on id
JQuery.each('.search-img-ctrl');
Put attributes in your markup with 'data' prefix (as Satpal said) and other thig you can use directly the selector
var locationli = jQuery(this).find("li["+elementToHide+"]");
This code reads the attribute of the first found element, but it does not filter on it:
var locationli = jQuery(this).find('li').attr(elementToHide);
A filter might look something like this:
var locationli = jQuery(this).find('li')
.filter(function(){
return $(this).attr(elementToHide);
});
But obviously closure's method is much shorter. And keypaul is right, using data- prefix is the right way to store your own metadata on elements.
the answers to use li[' + elementToHide + '] are good ones, but to help you understand what you are experiencing
let's break down this line of code:
var locationli = jQuery(this).find('li').attr(elementToHide);
as you know, jQuery(this).find('li') returns all of the decendants of this which are li's, and in your example, there are 14 of these.
What does .attr() return when applied to a set of 14 elements?
I guess it could return an array, a concatenation, who knows?, but the writers of jQuery decided to just return the attribute corresponding to the first element in the set. In this case, you are calling .attr(elementToHide) on <li>Name</li>. This element does not have the "file_type" attribute, therefore, you get an empty string in return.
Here's a quick fiddle to illustrate: http://jsfiddle.net/pmn4/B9bqK/
to solve your problem, use either the techniques described by #keypaul and #closure or use jQuery's filter method

Find if ancestor has a certain CSS class

I would like to know if there is an easier way to check if an element has an ancestor with a particular class.
Consider the following HTML code:
<ul id="uniqueID" class="parentClass">
<li class="subclassA">
<div class="subclassB">
<nobr>
MyText
</nobr>
</div>
</li>
<li class="subclassA"> ... </li>
<li class="subclassA"> ... </li>
<li class="subclassA"> ... </li>
</ul>
<div>other elements in this page which I want to select</div>
Right now, I can select the element MyText by using a jQuery selector checking the href for a particular format. What I can then do is do .parent() a known number of times (4) and then check the class attribute of that particular element that I've now moved to. While this is working just fine, I am curious if there is a better way to do it, perhaps one that lets me be a bit more dynamic?
PS. There are a lot of elements that I'm selecting that'll fit this $('[href *= index.php]') format, so I want to keep those but remove the ones that fall under the categorization where they are a descendant of a member of class listclass. Currently I'm just selecting all of the elements with the selector above, then using an if statement to check through and see if it fits this condition. Again, if there is a more efficient way to do this (perhaps select these certain elements in the first place?) I would love to hear about it.
Current code:
$('[href *= "index.php"]').each(function(){
if ($(this).parent().parent().parent().parent().attr('class') != 'parentClass'){
//do things
}
});
To generalise you can use
.closest(".parentClass")
You can use closest and is:
$('[href*="index.php"]').each(function(){
if ($(this).closest('ul').is('.parentClass')) {
//do things''
}
});
if($(this).parents("ul.parentClass").length == 0){
//do something
}

Selecting element to copy using jQuery Clone

I have a guide where each chapter is located in a separate LI of a UL. I am trying to use the jQuery Clone function to search through the parent UL that contains all of these 'chapter' LIs, and return those chapters that contain specific text.
Right now, I'm getting odd results, likely because it's copying elements at their smallest child, rather than just the entire div.
Also, each of these chapter LIs should only be returned once.
makeIntoSldieshowUL - UL that contains all 'chapters'
slideShowSlide - class name for each 'chapter'
searchResultsArea - Div in which to append 'chapters' that contain text
So far I have:
$("#makeIntoSlideshowUL").find(".slideShowSlide:contains('" + $(this).val() + "')").clone().appendTo("#searchResultsArea");
To give you an idea of the content I'm looking to clone, here is a brief sample
<ul id="makeIntoSlideshowUL">
<li class="slideShowSlide" id="0">
<div class="topicTitle">Cardholder responsibilities</div>
<p>Cardholders are responsible for ensuring proper use of the card. If your division or department has approved you for a Pro-Card, you must use the card responsibly in accordance with the following requirements:</p>
<ul>
<li>Purchase items for UCSC business use only</li>
<li>Never lend or share your Pro-Card</li>
<li>Purchase only allowable goods and services</li>
</ul>
</li>
<li class="slideShowSlide" id="1">
<div class="topicTitle"><strong>Restricted and Unallowable Pro-Card Purchases</strong></div>
<p>Some types of purchases are restricted are not allowed with the Pro-Card. Disputes with suppliers are initially handled by the Cardholder if, for example, they don't recognize a transaction on their statement, or the amount doesn't match their receipt. The Cardholder is responsible for contacting the supplier immediately to try to resolve the matter.</p>
</li>
Use jQuery's .children() method instead of .find() since it sounds like the .slideShowSlide elements are immediate children.
$("#makeIntoSlideshowUL").children(".slideShowSlide:contains('" + $(this).val() + "')").clone().appendTo("#searchResultsArea");
Or you could use the > child selector instead.
$("#makeIntoSlideshowUL > .slideShowSlide:contains('" + $(this).val() + "')").clone().appendTo("#searchResultsArea");
EDIT: At one point, you seem to refer to the chapters as divs. If they're a child of the <li> elements, you'll likely need something like:
$("#makeIntoSlideshowUL > li > .slideShowSlide:contains('"...
try using the has or contains selector
has will not change the current element on the jquery stack.
$("#makeIntoSlideshowUL")
.has(".slideShowSlide:contains('" + $(this).val() + "')")
.clone()
.appendTo("#searchResultsArea");

how to select 2nd <LI> element using javascript?

Using JavaScript, how can I dynamically change one of the list-items below from this:
<ul class="tabbernav">
<li class="tabberactive"><a title="All">All</a></li>
<li class=""><a title="One">One</a></li>
<li class=""><a title="Two">Two</a></li>
<li class=""><a title="Three">Three</a></li>
</ul>
to
<ul class="tabbernav">
<li class="tabberactive"><a title="All">All</a></li>
<li class=""><a title="One">One</a></li>
<li class=""><a title="Two">-----------NEW LIST ITEM CHANGED---------</a></li>
<li class=""><a title="Three">Three</a></li>
</ul>
I guess you could use getElementsByTagName inside of the ul to get all your list items inside an array. Then you can just edit the third element in your array, with index number 2.
var lItems = document.getElementsByTagName("ul").getElementsByTagName("li");
lItems[2].innerHTML = "<a title='Two'>----NEW LIST ITEM CHANGED-----</a>";
That will ofcourse get all ul elements on the page, and might lead to some strange results if you have more than two uls in your document. But you get the idea, right? Just ask some more if you don't understand what I'm trying to say.
Okay, the above code doesn't really work properly. I've modified my code a bit, but that also included a change in your HTML, as i presume you'll only have one ul "tabbernav", thus I changed it from class="tabbernav" to id="tabbernav". This is the code to do what you want.
var ul = document.getElementById("tabbernav");
var liArray = ul.getElementsByTagName("li");
for (var i = 0; i < liArray.length; i++) {
if(liArray[i].childNodes[0].title == "Two") {
liArray[i].innerHTML = "Your desired output";
}
}
Hope that helps you some more :)
I also suggest using jQuery, which makes selections like this trivial. In your case, you can use the :eq psuedo-selector to get the second line element:
$('.tabbernav li:eq(1)')
This selects the DOM element which is the second li (indexes start at 0) in an element with the class tabbernav. It returns a jQuery object which you can chain other methods to. Changing the inner HTML is done with .html('Your html here').
This is how you select the third Li element of your Ul list in pure JavaScript.
document.querySelectorAll("li")[2].innerHTML = "vasile";
Replace "Vasile" with your desired text.
var list = document.getElementsByTagName("li");
list[2].innerHTML = "<a title='Two'>------NEW LIST ITEM CHANGED----</a>";
this will work perfect
I know that this question is old but since it's still open, see how I modified the first answer. I feel someone else might need it.
>var lItems = document.getElementsByTagName("ul")[0];
>>var nth = lItems.getElementsByTagName("li")[2];
>>>nth.innerHTML = "<a title='Two'>----NEW LIST ITEM CHANGED-----> </a>";
So that basically solves it up by specifying the position of the lItems in particular to grab and in this case [0]. The code will not work properly if that position is missing because getElementsByTagName(NAME) returns a collection of html elements bearing that NAME specified. So that if you don't specify which among them all, the code fails.
If you like code reuse, see a function you can use for that. You just need to specify the parent element and its position and the childNode position and you get the same thing.
>var nthChild = function(parent, pos, childPos){
>>parent = document.getElementsByTagName(parent)[pos];
>>>return parent.children[childPos];
>>>>};
//used thus:
>nthChild("ul", 0, 2).innerHTML = "<a title='Two'>----NEW LIST ITEM CHANGED-----> </a>
";
How do you identify which <li> is the one you want to modify?
If you're doing it by index you could do something like this I think:
var list = document.getElementById("listid");
list.childNodes[2].innerHtml = "<a title='Two'>-----------NEW LIST ITEM CHANGED---------</a>";
Look into using a Javascript library such as JQuery. That will make your life a lot easier. Then you can do something like this:
$('li a[title=Two]').text('Changed Text Goes Here');
You'll need to check my syntax (not sure about the text() function), but it's easy enough to look up in JQuery's api docs.

Categories