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.
Related
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.
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.
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')
How do I select only the first instance of a link containing a specific href, the solution can be css or javascript, I would prefer css as I need to make styling changes once I have selected the right link but I'm not even sure css can do what I need.
<ul class="digital-downloads">
<li> Link</li>
<li> Link</li>
<li> Link</li>
<li> Link</li>
<li> Link</li>
</ul>
As you can see by the code above, the first two links aren't identical but they have the same ending. I need a way to select only the first instance of a link containing download_file=936 or download_file=935 or download_file=932 etc.
The number of li's will always be different as well as the number of same links so I can't select the third li for example as the link href wont always be 935 on the third li, it could be 936 or 932 depending on the situation.
You can apply a filter against a jQuery selection, looping over them while comparing against the href from before:
var last;
var firsts = $(".digital-downloads li a").filter(function(){
var link = this.href.match(/download_file=\d+$/)[0];
if (link == last)
return false; // we had this before, don't select
else {
last = link;
return true; // we've found a new one!
}
});
In short and taking care of mismatched regex:
…
var m = this.href.match(/download_file=\d+$/);
return m && m[0] != last && (last=m[0],true);
…
$('a[href*="download_file=935"]:first')
To do it in just CSS gets really complicated.
Using JavaScript:
var first = document.querySelector('a[href$="936"]');
first.className = 'first';
JS Fiddle demo.
This approach adds a CSS class-name in order to allow CSS styling, so should combine the benefits of each approach, so long as the browser supports document.querySelector().
Use Regex:
$(function() {
var count = 0;
$('a').each(function() {
var match = $(this).attr('href').match(/(.*download_file=\d+)/);
if (match !== null && count == 0) {
alert(match.pop());
count++;
}
});
});
http://jsfiddle.net/vzJVa/
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');
});