one function to fire on same class elements click - javascript

I'm trying to make controls for category list with sub-category and sub-sub-category lists.
Here's HTML:
<ul class="selectbox-ul">
<li>
<div>Category</div>
<ul class="selectbox-ul-child">
<li>
<div>Subcategory</div>
<ul class="selectbox-ul-child">
<li>
<div>Sub-subcategory</div>
</li>
</ul>
<span id="trigger">icon</span>
</li>
</ul>
<span id="trigger">icon</span>
</li>
....
</ul>
So my shot was to add class for ul.selectbox-ul-child :
var trigger = document.getElementById("trigger");
function subCatStatus() {
if(this.parentElement.children[1].className != "... expanded") {
this.parentElement.children[1].className += " expanded"
} else {
this.parentElement.children[1].className == "..."
};
};
trigger.addEventListener("click", subCatStatus);
And it works only for first span#trigger(that shows subcategories), next one (for sub-subcategories) does nothing (I've also tried to use .getElementsByClassName it didn't work for any of triggers) . So i'd like to get some explanation why doesn't this one work. And some advice how to make it work.

As others have already mentioned, you can't stack multiple elements with the same ID since document.getElementById() is not supposed to return more than one value.
You may try instead to assign the "trigger" class to each of those spans instead of IDs and then try the following code
var triggers = document.getElementsByClassName("trigger");
function subCatStatus() {
if(this.parentElement.children[1].className != "... expanded") {
this.parentElement.children[1].className += " expanded"
} else {
this.parentElement.children[1].className == "..."
};
};
for(var i = 0; i < triggers.length; i++) {
triggers[i].addEventListener("click", subCatStatus);
}

javascript getElementById returns only single element so it will only work with your first found element with the ID.
getElementsByClassName returns an array of found elements with the same class, so when adding listener to the event on element you would require to loop through this array and add individually to it.

Related

Dynamically add class based on page

I have a navagation list written with bootstrap css:
<ul class="nav navbar-nav navbar-right">
<li class="">Home</li>
<li class="">About</li>
</ul>
My question is how can I use javascript to add the class "active" to the "li" tags using javascript? I want it to have the active class on index.html for home and the same for about.html
Is this possible?
JavaScript
var siteList = document.URL.split("/");
var site = siteList[siteList.length - 1];
var list = document.getElementsByTagName("li");
for (var index = 0; index < list.length; index++) {
var item = list[index];
var link = item.firstElementChild;
var href = link ? String(link.href) : "-";
if (href.replace(".html","") === site) {
item.classList.add("open");
} else {
item.classList.remove("open");
}
}
Explanation
You can get the current URL using document.URL, you probably want the just the last part, so you'll have to split it and get the last part, which in your case will be index, about etc.
Then get all the li elements and iterate through them.
If they don't have an a child then ignore it.
If they do get the href attribute and remove the .html at the end.
If that text is the same as the site variable, then that means you should open the element, otherwise close it.
It's not clean, and there's probably a better way to do it, but:
HTML
<ul class="nav navbar-nav navbar-right">
<li id="navIndex" class="">Home</li>
<li id="navAbout" class="">About</li>
</ul>
JS (somewhere)
// Map ids with html page names
var pages = {
navIndex: "index.html",
navAbout: "about.html"
};
// Iterate over map
for(var property in pages) {
// Check to make sure that the property we're iterating on is one we defined
if(pages.hasOwnProperty(property)) {
// indexOf will be 0+ if it appears in the string
if(window.location.href.indexOf(pages[i]) > -1) {
// we can use property because we defined the map to be same as ids
// From https://stackoverflow.com/questions/2739667/add-another-class-to-a-div-with-javascript
var el = document.getElementById(property);
el.className += el.className ? ' active' : 'active';
break; // no need to keep iterating, we're done!
}
}
}
This is more or less a "dirty" approach because it requires more than just JavaScript to get to work (see Nick's answer for a cleaner implementation).
First we set identifiers on our <li> elements, then we map out those identifiers with their respective href attributes.
Once we have the <li>s mapped out, we then iterate over the map's keys (which we set to be the id attributes on our <li>s) and check if the href attribute is present within the site's window.location.href, if it is: add the active class and stop searching, otherwise we keep on trucking.

Can I use a For loop with a Nodelist?

Basically I have some HTML code that is a tree, I was traverse the Nodelist for it and and assign certain classes to nodes if they have children, here's a snippet:
<li id='test' class="parentNode">
<button class="customer-btn button"><a href='#'>Customer 6</a></button>
<ul>
<li>
<a href='#'>Customer A</a>
</li>
</ul>
</li>
<li class="parentNode">
<button class="customer-btn button"><a href='#'>Customer 7</a></button>
<ul>
<li>
<a href='#'> Customer A</a>
</li>
</ul>
</li>
This is my Javascript:
parent_list = document.getElementsByTagName("LI");
var i;
$(document).ready(function() {
for (i=0; i < parent_list.length; i++){
children = $(i).find('LI');
document.getElementById('check').innerHTML = children;
}
});
The for loop I have return [object Object], what's the best what to do this?
You don't need jQuery.
window.addEventListener('DOMContentLoaded', run );
function run() {
var allLIElements = document.getElementsByTagName('LI');
for( var i = 0; i < allLIElements.length; i++ ) {
var li = allLIElements[i];
if( li.firstElementChild != null ) {
li.classList.add('hasChildren');
}
}
}
Note that this will soon be unnecessary as CSS has the proposed :has() pseudo-class which you can use to select elements that meet some criteria.
https://developer.mozilla.org/en-US/docs/Web/CSS/:has
The :has() CSS pseudo-class represents an element if any of the selectors, relative to the:scope of the given element, passed as parameters, matches at least one element. The :has() pseudo-class takes a selector list as an argument.
Consider this style rule instead, it will match any li element that contains another element. No JavaScript required.
li:has(> *) { /* As of early 2017 no browser supports this selector yet! */
}
Since you're using jQuery you don't need a for loop
$('li').each(function(index, element){
if($(element).children().length > 0){
$(element).addClass('myClass')
}
})
You can use a for loop for anything, but your code isn't correct...assigning children to innerHTML isn't a compatible assignment, and doing it in a loop will just result in the element 'check' being assigned multiple times, not with an addition.
If you are using jQuery then use:
$('#check').append(children);

Build list when select option change

I'm trying to display the full list that have the same id that matches with the select option. But I can't figure out how to get the id from the attribute by using the name to be able to filter it.
The html example:
<select id='groopy' onchange='see();'>
<option>default</option>
<option>lista1</option>
<option>list1</option>
</select>
<ul id='grupo'>
<li id='list1' name="lista">Playground (Rangbhoomi)</li>
<li id='default' name="lista">Empire Made Me</li>
<li id='default' name="lista">Transmission</li>
<li id='lista1' name="lista">Hostel Room 131</li>
<li id='default' name="lista">A Disobedient Girl</li>
<li id='default' name="lista">Travels in the Land of Kubilai Khan</li>
<li id='list1' name="lista">The Indian Mutiny</li>
<li id='lista1' name="lista">Beauty and Sadness</li>
<li id='default' name="lista">The Collaborator</li>
<li id='list1' name="lista">I, Lalla</li>
<li id='default' name="lista">No Full Stops in India</li>
<li id='lista1' name="lista">For Lust of Knowing</li>
<li id='default' name="lista">On the Road to Kandahar</li>
</ul>
And the script I'm trying:
<script>
function see(){
var listita = document.getElementById('groopy').options[document.getElementById('groopy').selectedIndex].value;
var items = document.getElementsByName("lista");
var items_id = document.getElementsByName("lista").getAttribute('id');
if(listita==items_id){
for(var i = 0; i < items.length; i++)
{
document.getElementById("description").innerHTML = items[i].outerHTML;
}
}
}
onload= see();
</script>
By the way, the select and the ul are generated dynamically so I don't actually now the id's that could be provided. I'm trying a little different approach here .
When I manage to make the select filter work, the for stop working. WHY? I'm going crazy with this. Please help!!
Firstly you are having multiple elements with same id's which is wrong.. Cause getElementbyId will only fetch the first element encountered by it.
Replace then with class instead
Next you are overwriting the HTML for every iteration, so you will always have the last item appended to it.
Instead store that in a variable and append it after the for loop.
you need to bind your element with a change event, otherwise your call only works once when the page loads for the first time.
Try this
// Cache the selector as you are using it multiple times
var dropdown = document.getElementById('groopy');
function see() {
// set the selected option
var listita = dropdown.options[dropdown.selectedIndex].value
items = document.getElementsByClassName(listita);
html = '';
// For each item with class name, iterate over it and store in a string
for (var i = 0; i < items.length; i++) {
if (items[i].className == listita) {
console.log((items[i].outerHTML));
html += items[i].outerHTML
}
}
// set the html after the for loop
document.getElementById("description").innerHTML = html;
}
onload = see();
// attach the change event handler
dropdown.addEventListener('change', see);
Check Fiddle
try changing the id's to class in the li tags and use this function...
function see(){
var selectedVal = document.getElementById('groopy').options[document.getElementById('groopy').selectedIndex]. value;
var items = document.getElementsByClassName(selectedVal);
var html = '';
for(var i = 0; i < elements.length; i++)
{
html += items[i].outerHTML;
}
document.getElementById("description").innerHTML = html;
}
onload= see();
Step1 - Change id to class
Step2 - Traverse DOM elements with jQuery class selector. In this way replace document.getElementId('id) with $('.className')

Click speech bubble to display list items in order - Not working in IE8

I have a list in my HTML with a number of quotes. I have also got a div called "speech" which is styled to look like a speech bubble.
When the program is run the first quote is displayed in the bubble and when it is clicked it skips through the list of quotes.
My issue is that in IE8 nothing is displayed in the bubble and I cannot work out why. Can someone tell me whether I am doing something wrong, or advise me on how to do a better.
Here is the list...
<ul class="facts">
<li>
<span>What goes ho-ho whoosh, ho-ho whoosh?<br/>Santa caught in a revolving door.</span>
</li>
<li>
<span>Whats white and goes up? <br/> A confused snowflake.</span>
</li>
<li>
<span>Which Christmas carols do parents like best? <br/> Silent Night.</span>
</li>
<li>
<span>How did the snowman get to school?<br/>On his icicle.</span>
</li>
<li>
<span>What do you call a three-legged donkey?<br/> A wonkey.</span>
</li>
</ul>
and here is the script...
jQuery.fn.extend({
rnditem : function(l) {
var a = [],
current = jQuery(this).html().trim(),
index,
next;
Array.prototype.forEach.call(l, function(item) {
a.push(jQuery(item).html()
jQuery.trim();
});
index = a.indexOf(current) + 1;
if (index < 0 || index >= l.length) {
index = 0;
}
next = (l.eq(index).html());
jQuery(this).html(next);
return this;
}
});
jQuery(document).ready(function () {
var list = jQuery(".facts").find("li");
jQuery(document.body).on('click','.speech', function () {
jQuery(this).rnditem(list);
});
jQuery(".speech").trigger("click");
});
Fiddle: http://jsfiddle.net/S79qp/425/
Looking at the Fiddle, I see different code than what is posted above.
The lines below
current = jQuery(this).html().trim(),
and
a.push(jQuery(item).html().trim());
would throw an error that would be something like
Object doesn't support property or method 'trim'
because IE8 does not have trim built in, so use jQuery.trim()

How manage elements class name by any expression in jquery in mvc4 application?

There are <li> elements that one of them has class="current". I want to decide which of <li> is current. Here is view:
#{
int k = 10; //this will change in every request
}
<ul class="car">
#foreach (var item in modelCount)
{
<li
#{if (item.Id == k) { <text>class="current"</text>} }>
#item.Name
</li>
}
</ul>
This works, but the first <li> is always current by default. When first element is current by my expression, everything is ok, but otherwise, <ul> has 2 current <li>.
How can I solve this problem with jquery function?
Edit:
I need:
If <li> elements have 2 current classes, to remove the first of them.
function removeDuplicateCurrentClass(){
var currentListElements = [];
$('ul.car li').each(function(){
if($(this).hasClass('current'))
currentListElements.push(this);
});
if(currentListElements.length > 1)
$(currentListElements[0]).removeClass('current');
//if more than one current class exist: remove the first one
}
$(function(){
removeDuplicateCurrentClass();
//or simply
if($('ul.car li.current').length > 1)
$('ul.car li.current:first').removeClass('current');
});

Categories