I'm working on a one-page site that has several "articles". Each div has class "article" and id "first", "second", "third", etc. I have a standard menu:
<ul id="nav">
<li id="n1"></li>
<li id="n2"></li>
<li id="n3"></li>
//...etc
</ul>
What I need to do is assign the class "active" to the li tag w/id n1 when the current article has id "first" (and unassign active class from all other li tags); assign active class to li tag w/id n2 when current article has id "second"; etc.
I would appreciate any help... I'm really stuck on this one. TIA for your help.
P.S. I'm using the following code to, in essence, assign an active class to the currently viewed article:
$('#first, #second, #third, ...).bind('inview', function (event, visible) {
if (visible == true) {
$(this).addClass("inview");
} else {
$(this).removeClass("inview");
}
});
There are more ways to do it. This is one, which may give you ideas:
var navrefs= document.getElementById('nav').getElementsByTagName('a');
for (var i=0, len = navrefs.length;i<len;i++){
if (location.hash == navrefs[i].href){
navrefs[i].parentNode.className = 'active';
} else {
navrefs[i].parentNode.className = '';
}
}
Here's an example of the following →
The following assumes you have the class current on your currently active article page:
var articles = document.getElementsByClassName('article'),
al = articles.length,
which, i;
for (i = 0; i < al; i++) {
if (/current/.test(articles[i].className)) {
which = articles[i].id;
}
}
var atags = document.getElementsByTagName('a'),
al2 = atags.length;
for (i = 0; i < al2; i++) {
if (atags[i].href.search(which) > -1) {
atags[i].parentNode.className = 'active';
} else {
atags[i].parentNode.className = '';
}
}
Since it's to achieve a CSS effect, why not just right CSS like this:
.first #n1,
.second #n2,
.third #n3,
...etc
{
CSS for active article
}
and then just make the JS in charge of setting the appropriate class on the wrapper/body, e.g.
<li id="n1"></li>
etc.
...
function setC(str){
document.getElementById('wrapper').className=str;
}
This would would reduce the demand on the JS engine
Related
I've created a script to fade out all items in a list when one element in the list is hovered.
I've sort of got it to work - but the last item in the list doesn't fade in (when the last item in the list is hovered all items are faded) - the rest of the list though works correctly.
My script is here -
var activeItems = document.querySelectorAll('.item');
for (i = 0; i < activeItems.length; i++) {
activeItems[i].addEventListener("mouseover", fadeOutItems, false);
activeItems[i].addEventListener("mouseout", resetListStyles, false);
function fadeOutItems() {
for (i = 0; i < activeItems.length; i++) {
this.setAttribute("class", "item fade-in");
activeItems[i].setAttribute("class", "item fade-out");
}
}
function resetListStyles() {
for (i = 0; i < activeItems.length; i++) {
activeItems[i].setAttribute("class", "item");
}
}
}
And have created a fiddle here -
https://jsfiddle.net/1opq1eyj/1/
Any advice on where i'm going wrong would be much appreciated.
Also would be grateful for any advice on how the script could have been improved upon.
Thanks,
You have wrong ordering
First you should deselect all and then you should make this.setAttribute
function fadeOutItems() {
for (i = 0; i < activeItems.length; i++) {
activeItems[i].setAttribute("class", "item fade-out");
this.setAttribute("class", "item fade-in");
}
}
update link
As mentioned, the fade-in attribute is set each time inside the loop. For the last item, that means that activeItems[i] will overwrite that last class-setting. The 'fade-in' setting can be placed outside the loop.
function fadeOutItems() {
for (i = 0; i < activeItems.length; i++) {
activeItems[i].setAttribute("class", "item fade-out");
}
this.setAttribute("class", "item fade-in");
}
As for the possible alternatives. The same can be done with pure css with a single selector:
.item-list:hover > .item:not(:hover) {
opacity: 0.6;
}
This sets the opacity for all .items inside the .item-list div, except the one being hovered. fiddle Option 1
However, since width of the container is 100%, the effect may trigger somewhat to soon, (depending on the total layout).
To be on the safe side, you can add an extra class to the container once one of the child items is hovered with JS (as long as CSS parent selectors are not in basic support) and base the css on that class:
JS:
let activeItems = document.querySelectorAll('.item'),
itemList = document.querySelector('.item-list');
for (i = 0; i < activeItems.length; i++) {
activeItems[i].addEventListener("mouseover", function(){itemList.classList.add('fade');}, false);
activeItems[i].addEventListener("mouseout", function(){itemList.classList.remove('fade');}, false);
}
CSS:
.fade:hover > .item:not(:hover) {
opacity: 0.6;
}
Fiddle option 2
I have the following HTML
<li class="navUser-item navUser-item--account">
<a class="navUser-action" href="/login.php">Login</a>
<span class="navUser-or">or</span> // Hide this
<a class="navUser-action" href="/login.php?action=create_account">Sign Up</a> // Hide this
</li>
I'm trying to hide the last ahref and the span tag using Javascript. I can't seem to get this to work as expected, I have tried the following:
I was hoping I could extend this to look for a given Href and if its of such value i.e create account then hide it however no such luck.
var litag = document.getElementsByClassName('navUser-item navUser-item--account'), i;
for (i = 0; i < litag.length; i += 1) {
litag[i].style.display = 'none';
}
I then tried the following:
document.getElementsByClassName('navUser-item navUser-item--account a:nth-last-child(2)')[0].style.visibility = 'hidden';
tried finding the second a and yet didn't work, can someone shed some light into how I go about hiding the span and the last a href?
You can use the querySelector like this
var link = document.querySelector('a[href="/login.php?action=create_account"]');
link.style.display = 'none';
https://jsfiddle.net/mtinra/nsznwbgk/
Edit: or use querySelectorAll to select both span and a
var myEl = document.querySelectorAll('.navUser-item > span, a[href="/login.php?action=create_account"]');
for (var el of myEl){
el.style.display = 'none';
}
Supposing you have only one navigation menu (I'm choosing the first element with class name navUser-item here var childnodes = mainNav[0].childNodes;) you could do something like this:
var mainNav = document.getElementsByClassName('navUser-item');
var childnodes = mainNav[0].childNodes;
for (i = 0; i < childnodes.length; i += 1) {
if (childnodes[i].className && childnodes[i].className.match('navUser-action')) {
if (childnodes[i].href && childnodes[i].href.indexOf('create_account') !== -1) {
childnodes[i].style.display = 'none';
}
} else if (childnodes[i].className && childnodes[i].className.match('navUser-or')) {
childnodes[i].style.display = 'none';
}
}
<li class="navUser-item navUser-item--account">
<a class="navUser-action" href="/login.php">Login</a>
<span class="navUser-or">or</span>
<a class="navUser-action" href="/login.php?action=create_account">Sign Up</a>
</li>
I generally find it better to change classes and use CSS for toggling display of items.
var links = document.getElementById('link-list').children;
for (var i = 0; i < links.length; i++) {
if (links[i].className.match(/(?:^|\s)navUser-register(?!\S)/)) {
console.log(links[i]);
links[i].className = links[i].className.match(/show/) ?
links[i].className.replace(/show/, 'hide') :
links[i].className.replace(/hide/, 'show');
}
}
.hide {
display: none;
}
.show {
display: inline-block;
}
<li id="link-list" class="navUser-item navUser-item--account">
<a class="navUser-action" href="/login.php">Login</a>
<span class="navUser-or navUser-register show">or</span>
<a class="navUser-action navUser-register show" href="/login.php?action=create_account">Sign Up</a>
</li>
EDIT: You could also surround desired elements in a div tag and set it's display property depending on the current page. In my opinion this would be the easiest way.
<div id="register-page" class="show"> ... elements ... </div>
And your JavaScript part would include just a search for element by this ID and toggling it's class from show to hide.
var div = document.getElementById('register-page')
div.className.match(/show/) ? div.className.replace(/show/, 'hide') : div.replace(/hide/, 'show');
I have a page full of links and they're paired. What I would like to do is once I've clicked a question mark (for help) the first 2 pairs are selected, then the next 2 and so on. The problem is that the links are created randomly on the page. I have the following code which selects the first link and its pair.
$(".main .container a:first").css("color", "#0c0");
var valid = $(".main .container a:first").attr("class").split(" ");
var links = $(".main .container a");
for (i = 0; i < links.length; i ++) {
var attributes = $(links[i]).attr("class").split(" ");
if (attributes[1] == valid[1]) {
$(links[i]).eq(0).css("color", "#0c0");
}
}
EDIT:
$(".help a").on("click", function()
{
var unchecked = $(".main .container a:not(.selected)");
var valid = unchecked.eq(0).attr("class").split(" ");
var links = $(".main .container a");
unchecked.eq(0).addClass("selected");
for (i = 0; i < links.length; i ++) {
var attributes = $(links[i]).attr("class").split(" ");
if (attributes[1] == valid[1]) {
$(links[i]).eq(0).addClass("selected");
}
}
});
Looks like you can simply manage that by adding a CSS class to the elements that were already "selected".
Check this simple test I did at JSFiddle: http://jsfiddle.net/L4rUM/1/
// When you click the "?"
$('button#btnHelp').on('click', function(){
// Gathers the list of ALL <a> elements that were not "selected" yet
var uncheckeds = $('.main .container > a:not(.selected)');
// If you have at least 2 available elements
if(uncheckeds.size() > 1){
// Adds the .selected CSS class to first 2 non-selected elements
uncheckeds.eq(0).addClass('selected');
uncheckeds.eq(1).addClass('selected');
}
});
And the CSS manipulation you are doing is now controlled by just adding the CSS class:
.selected { color: #0c0; }
I hope this helps and solve your problem :)
I have a drop down menu that is supposed to work with four different menu choices, each given the same class. But my code is not working. I want it to work with both chrome and IE. The situation where it crashes is in my init method. The console complains as following: Object # has no method 'getElementsByTagName'. Any solution ?
function hideorShowField(list) {
var nodes = list.getElementsByTagName("li");
for (i = 1; i < nodes.length; i++) {
if (nodes[i].style.display == 'none') {
nodes[i].style.display = 'block';
}
else {
nodes[i].style.display = 'none';
}
}
}
function init() {
var list = document.getElementsByClassName("undermeny");
list1.getElementsByTagName("li")[0].onclick = function () {
hideorShowField(list);
};
}
window.onload = init;
My html code:
<ul class="undermeny" >
<li>Opinion</li>
<li>Ledare</li>
<li>Aktuella frågor</li>
<li>Per T Ohlsson</li>
<li>Magda Forsberg</li>
</ul>
<ul class="undermeny" >
<li>Lokalt/Globalt</li>
<li>Malmö</li>
<li>Lund</li>
<li>Limhamn</li>
<li>Burlöv</li>
<li>Eslöv</li>
<li>Höör</li>
<li>Kävlinge</li>
<li>Lomma</li>
<li>Svedala</li>
<li>Staffanstorp</li>
<li>Trelleborg</li>
<li>Vellinge</li>
<li>Sverige</li>
<li>Öresund</li>
<li>Världen</li>
<li>Väder</li>
</ul>
<ul class="undermeny" >
<li>Ekonomi</li>
<li>Nyheter</li>
<li>Privata pengar</li>
<li>Börs</li>
<li>Fonder</li>
</ul>
<ul class="undermeny">
<li>Sport</li>
<li>Fotboll</li>
<li>Ishockey</li>
<li>Handboll</li>
<li>Fridrott</li>
</ul>
getElementsByClassName() returns a nodeList as well as getElementsByTagName(). So you probably need this:
list[0].getElementsByTagName("li")[0].onclick = function () {...};
Or you've to iterate over the listto attach eventhandlers to all 1st lielements within uls with class undermeny. That'll be something like this:
for (var n = 0; n < list.length; n++) {
list[n].getElementsByTagName("li")[0].onclick = function () {...};
}
list1 has not been initialized (although list has). Fix the typo, and then you can iterate over the items in list, calling getElementsByTagName() on each.
I have some nav links like so:
<ul id="nav">
<li>Home
<li>About
<li>Contact
</ul>
How can I add a CSS class called active to the opening <li> tag of the list item that contains the a href whose value matches the current url?
For example, if the current page the user is on is about.html then the nav should look like this:
<ul id="nav">
<li>Home
<li class="active">About
<li>Contact
</ul>
Please note:
the urls can have additional parameters like:
about.html?foo=bar&bar=loo
so whatever is used to detect the url should not take parameters into consideration but just the page name and extensions.
I would prefer to achieve this in plain JavaScipt since I am not using jQuery for anything else on the site, but either is fine.
Edit
The index page had index.html in the url when it's landed on from another page but if the domain is types it shows as:
http://www.sitename.com/
so if no page is specified the active class should be attached to the home list's tag.
jQuery:
if(window.location.pathname === '') {
$('#nav li:first-child').addClass('active');
}
else {
var path = window.location.pathname;
path = path.substr(path.lastIndexOf('/') + 1);
$('#nav li').filter(function(index) {
return path === $(this).children('a').attr('href');
}).addClass('active');
}
Plain JavaScript:
var menu_elements = document.getElementById('nav').children;
if(window.location.pathname === '') {
menu_elements[0].className += ' active';
}
else {
var path = window.location.pathname;
path = path.substr(path.lastIndexOf('/') + 1);
for(var i = menu_elements.length; i--;) {
var element = menu_elements[i];
var a = element.children[0];
if(a.href === path) {
element.className += ' active';
break;
}
}
}
Note: children[] is not supported by FF 3.0. If you experience any problems with children, you can substitute this with an appropriate getElementsByTagName call.
Simple version
window.onload=function() {
var activeLi;
if (location.pathname) {
var fileName = location.pathname.substring(pathname.lastIndexof('/')+1);
/* just take the start -
not handling filenames that are substrings of other filenames
nor filenames with more than one dot. */
fileName = fileName.split('.')[0];
var links = document.getElementById('nav').getElementsByTagName('a');
for (var i=0;i<links.length;i++) {
if (links[i].href.indexOf(fileName)==0) { // starts with filename
activeLi = links[i].parentNode;
break;
}
}
}
else { // no page given
activeLi = document.getElementById('nav').getElementsByTagName('li')[0];
}
if (activeLi) activeLi.className="active";
}
More complex would be to ADD the active to className, but if you do not have other classes on the LI, then it is not needed - but if you use jQuery it is much simpler.
//Get sub-domain url
var currentUrl = window.location.href,
splitUrlArr = currentUrl.replace(/\?.*/,'').split('\/');
subDomainUrl = splitUrlArr[splitUrlArr.length-1];
//if url matches the site home url, add classname 'active' to first li
if(splitUrlArr.join('\/') == currentUrl) {
document.getElementById('nav').getElementsByTagName('li')[0].className = "active";
}else {
//Find the matching href and add className 'active' to its parent li
var targetLi = null;
var links = document.getElementById('nav').getElementsByTagName('a');
for (var i=0; i < links.length; i++) {
if (links[i].href === subDomainUrl) {
targetLi = links[i].parentNode;
break;
}
}
if(targetLi) { targetLi.className = "active"; }
}