I'm trying to change the colour of text in my a list on a button press.
My trouble is when I'm trying to get the colour to reset on other list items before applying the 'selected' colour. See code:
function changeNavColour(clicked_id) {
document.getElementById("navAbout").style.color = "black";
document.getElementById("navShows").style.color = "black";
document.getElementById("navPrices").style.color = "black";
clicked_id.style.color = "#c60707";
}
<html>
<nav class='navigation' id='navigation'>
<ul class="navbar" id='nav'>
<li class="navbar-item" id='navAbout'>ABOUT</li>
<li class='navbar-item'>-</li>
<li class="navbar-item" id='navShows'>SHOWS</li>
<li class='navbar-item'>-</li>
<li class="navbar-item" id='navPrices'>PRICES</li>
</ul>
</nav>
</html>
So at the moment, the text turns red on click, however the other colours (if they're already red) do not get set to black and stay red.
Any ideas?
Thanks
As Alex already pointed out, your text is between the <a></a> tags. Easiest way to fix it is to add firstChild (like document.getElementById("navAbout").firstChild.style.color ="black" ). A better way to do it is using classes, because using style property usually not a good practice (read more about adding and removing classes here).
you can use something like this, or change the selector however you need using querySelectorAll:
function changeNavColour(clicked_id) {
x=document.querySelectorAll("a")
x.forEach(function(item){
item.style.color = "black";
})
clicked_id.style.color = "#c60707";
}
Hope this help.
Your event handler is currently dealing with a HTMLAnchorElement reference directly, not an id attribute value. See Javascript snippet comments.
function changeNavColour(anchorElement) {
// Look for every anchor tag within the navigation list
document.getElementById('navigation').querySelectorAll('a').forEach(a => {
// Change their color depending on supplied anchor reference (the event target reference)
a.style.color = a === anchorElement ? "#c60707" : 'black'
});
}
<!-- Keep your markup as is -->
<nav class='navigation' id='navigation'>
<ul class="navbar" id='nav'>
<li class="navbar-item" id='navAbout'>ABOUT</li>
<li class='navbar-item'>-</li>
<li class="navbar-item" id='navShows'>SHOWS</li>
<li class='navbar-item'>-</li>
<li class="navbar-item" id='navPrices'>PRICES</li>
</ul>
</nav>
EDIT
As is, individual anchors ids are not hardcoded anymore, this means the Javascript function will still work when you add or remove items from navigation list.
But we can go a step further and implement event delegation strategy instead.
There are several advantages, it makes possible to add navigation item(s) later programmaticaly and automaticaly benefit from the ul#navigation registered click event handler behavior. It also limits the number of event listeners attached to the DOM (that's generally not an issue but it is optimization and may be considered as best practice IMO), :
document.getElementById('navigation').addEventListener('click', function(evt) {
const anchor = evt.target instanceof HTMLAnchorElement && evt.target;
if (anchor) {
// Look for every anchor tag within the navigation list
// Event handler is attached to the ul#navigation
// so "this" keyword is a reference to the uordered list HTML element
this.querySelectorAll('a').forEach(a => {
// Change their color depending on supplied anchor reference (the event target reference)
a.style.color = a === anchor ? "#c60707" : 'black'
});
}
});
<!-- Keep your markup as is -->
<nav class='navigation' id='navigation'>
<ul class="navbar" id='nav'>
<li class="navbar-item" id='navAbout'>ABOUT</li>
<li class='navbar-item'>-</li>
<li class="navbar-item" id='navShows'>SHOWS</li>
<li class='navbar-item'>-</li>
<li class="navbar-item" id='navPrices'>PRICES</li>
</ul>
</nav>
Related
I made the remove item button with the js code while doing the to-do list project.
removing items which are earlier made in Code editor works well.
*so in browser preview, I typed and added "new To-do" item to list.*
but removing "new To-do" item doesn't work. even chrome Devtool shows no error. new item's tag and className is all correct.
p.s : don't need to worry about 'X' or X icon. I skipped for uploading.
[HTML]
<ul id="listUl">
<li>Meet George <span class="remove"></span></li>
<li>Pay Bills <span class="remove"></span></li>
<li>new To-do <span class="remove"></span></li>
</ul>
[JS] - removing part.
var remove = document.getElementsByClassName('remove');
for (var i = 0; i < remove.length; i++) {
remove[i].onclick = function() {
var target = document.getElementById('listUl');
target.removeChild(this.parentElement);
}
}
That's because every new item you add does not have any listener on them (remove[i].onclick = function(){...}). So when you click on them, nothing happens. You need to add the onClick listener to every new items, like you are doing for the initial ones.
Add the onClick listener to ul tag. Check
if(e.target && e.target.nodeName == "LI") {
//Your code here
}
I rewrote your code:
There is only one (delegated) event assigned now to the <ul> parent element, it will trigger only, when the click happens on an element with class="remove". In that case it is the <span> element which I also made a bit more visible by adding an "X" inside. Right, when the action occurs I have to go up two levels until I reach the parent of the actual element I want to remove: the <li> element.
var i=3,UL=document.querySelector('#listUl');
UL.addEventListener('click',function(ev){
// ev.target - this element was clicked on
if (ev.target.classList.contains('remove'))
// ev.target.parentNode - this must be the <li> element
// ev.target.parentNode.parentNode - and this is the <ul> element
ev.target.parentNode.parentNode.removeChild(ev.target.parentNode);
})
document.querySelector('#addTask').addEventListener('click',
function(){UL.innerHTML+='<li>and task no. '+(++i)+'... <span class="remove">X</span></li>'})
<button id="addTask">add another task</button>
<ul id="listUl">
<li>Meet George <span class="remove">X</span></li>
<li>Pay Bills <span class="remove">X</span></li>
<li>new To-do <span class="remove">X</span></li>
</ul>
The "add" button demonstrates how newly created<li>s also respond to clicks on the "remove"-span.
It looks like that the JS code only adds delete function to these todos that already exists when the page loads. Newly added todos will not have onclick callback function attached if you only execute your js once when the pages loads.
The workaround here is to attach the callback function every time you create a TODO element.
Your newly (dynamically) added DOM elements don't have event listeners attached to them, because on page load, you only iterated (looped) only currently available elements, so You may try the following approach (Read About Event Delegation):
document.getElementById('listUl').addEventListener('click', function(e) {
if (e.target.nodeName == 'LI') {
e.target.parentNode.removeChild(e.target);
}
});
<ul id="listUl">
<li>Meet George <span class="remove">X</span></li>
<li>Pay Bills <span class="remove">X</span></li>
<li>new To-do <span class="remove">X</span></li>
</ul>
if you have jquery added to your page, you can do it like
$('#listUl').on('click', 'span.remove', function() {
$(this).parent().remove();
});
$('.add').click(function() {
$("#listUl").append("<li>Whatever Content <span class='remove'>x</span</li>");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul id="listUl">
<li>Meet George <span class="remove">x</span></li>
<li>Pay Bills <span class="remove">x</span></li>
<li>new To-do <span class="remove">x</span></li>
</ul>
<butoon class="add">Add</butoon>
I am working through an issue where I have a menu that is mutli levels deep. I am trying to get it so that if a first level element is clicked (add class .ubermenu-active) it will look for any other first level element with that class and remove it.
$('.ubermenu-item-level-0 span').on('click',function() {
var target = $('.ubermenu-item-level-0');
$('.ubermenu li.ubermenu-item-level-0').removeClass('ubermenu-active');
target.parents('.ubermenu li.ubermenu-item-level-0').toggleClass('ubermenu-active');
});
HTML (mock):
<ul class="ubermenu">
<li class="ubermenu-item-level-0">
<a class="ubermenu-target">
<span class="ubermenu-target-title">Level 1</span>
</a>
<ul class="ubermenu-submenu">
<li class="ubermenu-item-level-1">
<a class="ubermenu-target">
<span class="ubermenu-target-title">Level 2</span>
</a>
</li>
<li class="ubermenu-item-level-1">
<a class="ubermenu-target">
<span class="ubermenu-target-title">Level 2</span>
</a>
<ul class="ubermenu-submenu">
<li class="ubermenu-item-level-2">
<a class="ubermenu-target">
<span class="ubermenu-target-title">Level 3</span>
</a>
</li>
</ul>
</li>
</ul>
<li class="ubermenu-item-level-0">
<a class="ubermenu-target">
<span class="ubermenu-target-title">Level 1</span>
</a>
</li>
</li>
</ul>
Right now if any of the sub elements are clicked the parent closes
Currently your target var is selecting all elements with class .ubermenu-item-level-0, so when you toggle the class you're toggling all parent elements. Your target var should be something relative to the element clicked, like var target = $(this).closest('.ubermenu-item-level-0');
So, your sample HTML isn't what I expected to see . . . specifically, your second <li class="ubermenu-item-level-0" . . . element is currently showing as a child under the first one, while your description makes it sound like they should be siblings.
For the sake of the solution I'm going to assume that those two lis are supposed to be siblings of each other and that somehow the code got mixed up. :)
So, here's how I would handle it . . .
var sFirstLevelMenuClass = ".ubermenu-item-level-0";
var sActiveMenuClass = "ubermenu-active";
var $firstLevelMenuOptions = $(".ubermenu").find(sFirstLevelMenuClass);
$firstLevelMenuOptions.children("a").children("span").on("click", function() {
$firstLevelMenuOptions.removeClass(sActiveMenuClass);
$(this).closest(sFirstLevelMenuClass).addClass(sActiveMenuClass);
});
Basically, I've simplified your logic and fixed one small issue that you had in your jQuery code.
Detailed Explanation
The issue was that when you used $('.ubermenu-item-level-0 span') as your selector for your change event. That translates to "any span element that is a descendant of a ubermenu-item-level-0 element". So, in addition to the spans that are directly under the ubermenu-item-level-0 list items, it was also picking up the ones under the ubermenu-item-level-1 and ubermenu-item-level-2 elements, since they are also descendants.
So, I changed your selector to $firstLevelMenuOptions.children("a").children("span") which translates to "all spans, that are direct children of an a, that is the direct child of an ubermenu-item-level-0 element" (Note: $firstLevelMenuOptions is set to equal $(".ubermenu").find(".ubermenu-active"); through the logic earlier in the code). This stops the selector from picking up the lower level spans.
Outside of that and trimming down some of your selectors to be more efficient, the only other thing that I changed was the flow of how the ubermenu-active class is manipulated. In my solution, there are two steps:
Remove ubermenu-active from ALL ubermenu-item-level-0 elements
Add ubermenu-active to the closest ubermenu-item-level-0 element to the span that was clicked (i.e., $(this))
That basically resets the list and then reselects the appropriate menu item to me active.
You can stop the event propagation:
event.stopPropagation()
Without seeing the full HTML, I was going to suggest trying this:
$('.ubermenu-item-level-0 span').on('click',function(e) {
e.stopPropagation();
var target = $('.ubermenu-item-level-0');
$('.ubermenu li.ubermenu-item-level-0').removeClass('ubermenu-active');
target.parents('.ubermenu li.ubermenu-item-level-0').toggleClass('ubermenu-active');
});
I'm trying to change the CSS of the cursor to default on a a href link of # on the menu-item-4082 "About" link below. And I don't know why this seemingly simple function doesn't want to work.
Must be something simple I'm not seeing. Is my CSS selector correct?
Or is there a different or better way to change the CSS with jQuery? What about removing the href="#" as well?
Fiddle: http://jsfiddle.net/2nbad1gc/
Function:
$("li#menu-item-4082 .not-click").css("cursor","default");
HTML
<ul id="menu-main-menu-bar">
<li id="menu-item-217" class="menu-item">
Home
</li>
<li id="menu-item-4082" class="menu-item menu-item-type-custom
menu-item-object-custom menu-item-has-children menu-item-4082
has-dropdown not-click">
About
</li>
<ul class="dropdown">
<li id="menu-item-158" class="menu-item menu-item-158">
Values
</li>
<li id="menu-item-4083" class="menu-item menu-item-4083">
Why
</li>
</ul>
Is my CSS selector correct?
No, it's incorrect. It should be:
$("li#menu-item-4082.not-click a").css("cursor","default");
You were trying to select the child of li#menu-item-4082 whose class is not-click. When in fact, the li itself had the class .not-click.
Remove the space between $("li#menu-item-4082 .not-click").
As a side note, I'd suggest adding a class rather than adding inline CSS.
$("li#menu-item-4082.not-click a").addClass('default-cursor');
.default-cursor {
cursor: default;
}
.. you could also remove the href attribute completely:
$("li#menu-item-4082.not-click a").removeAttr('href');
If you wanted to avoid jQuery completely, you could also remove the href attribute using plain JS:
Single element:
document.querySelector('#menu-main-menu-bar .not-click a').removeAttribute('href');
Multiple elements:
var anchors = document.querySelectorAll('#menu-main-menu-bar .not-click a');
Array.prototype.forEach.call(anchors, function (el, i) {
el.removeAttribute('href');
});
or you could avoid JS and just use CSS:
li#menu-item-4082.not-click a {
cursor: default;
}
Just use simple css
.not-click a{
cursor: default;
}
http://jsfiddle.net/2nbad1gc/14/
Your selector needs to be modified to
$('li#menu-item-4082.not-click a').css("cursor", "default");
Usually it is recommended to add a class to the HTML element that sets cursor to default rather than directly change the CSS with jQuery like this.
Using Firebug I have found that the Dynatree plugin changes the following code:
<li id="id3.1" class="expanded">Menu 1
<ul>
<li id="id3.1.1">Sub-menu 1</li>
</ul>
</li>
To this:
<li class="">
<span class="dynatree-node dynatree-exp-c dynatree-ico-c">
<span class="dynatree-connector"></span>
<span class="dynatree-icon"></span>
<a class="dynatree-title" href="#">Sub-menu 1</a>
</span>
</li>
So when I try to make a click event on the id="id3.1.1" nothing happens because this id doesn't exist anymore.
I made a search here and found the onActivate option that will make my click happen on the menu:
$("#treeMenu").dynatree({
onActivate: function(node){
var menuTitle = node.data.title;
alert(menuTitle);
}
});
My question: Is this the only way to do the click event using Dynatree?
Well I think that is the best option, because it uses the API of the plugin, but of course you could still attach an event to the <a> like this:
$('a.dynatree-title').live('click', function(e){
//here e.target is the link you have clicked
});
I am trying to formulate a selector to select a set of visible elements. Our application uses the Prototype JavaScript framework, version 1.6.0.3.
The markup I'm working with is as follows:
<ul>
<li style="display:none;">1 Hidden</li>
<li style="display:none;">2 Hidden</li>
<li style="">3 Visible</li>
<li style="display:none;">4 Hidden</li>
<li style="display:none;">5 Hidden</li>
<li style="display:none;">6 Hidden</li>
<li>7 Visible</li>
<li style="">8 Visible</li>
</ul>
As you can see, some elements may have a style attribute, but only the hidden ones contain the string "display:none;". I need to select the <li> elements that are visible, where visibility is defined as "does not contain display:none".
What I've tried to far:
var visibleItems = $$('li[style*="display:none"]'); // Yields: [ ]
var visibleItems = $$('li[style*="display"]'); // Yields: [li, li, li, li, li], but isn't specific enough
Ideas? Ideally I'd like this to be as compact as possible, but I'll take what I can get.
Yes, I know that jQuery can do this but I do not want to introduce another framework in to this application since much of it already depends on Prototype.
You can filter the items using the findAll function:
var notVisible = $$('li').findAll(function(el) { return !el.visible(); });