I want to change certain names in my UL using a button - javascript

I want to change 9 of the names in the UL to a red font using a on click button, while the other 3 names remain in a black font. And I want a button to reset the red fonts back to their original font. Can anyone help?
var title = document.getElementById("title");
var buttons = document.getElementsByClassName("btn");
var buttons = document.getElementsByClassName("btn2");
for (var btnIndex = 0; btnIndex < buttons.length; btnIndex++) {
buttons[btnIndex].onclick = function() {
title.style.color = this.getAttribute('data-color');
}
} else {
title1.style.color = this.getAttribute('data-color');
}
<ul>
<li id="title">John</li>
<li id="title">Jack</li>
<li id="title">Joe</li>
<li id="title1">Jim</li>
<li id="title">David</li>
<li id="title">Sam</li>
<li id="title1">Jay</li>
<li id="title">Frank</li>
<li id="title">Tim</li>
<li id="title">Zack</li>
<li id="title">Lewis</li>
<li id="title1">Danny</li>
<button class="btn" data-color="red">Change 9 names to red</button>
<button class="btn2" data-color="black">Reset</button>
</ul>

There are a couple of problems with your markup, which I'll address below, but to answer your actual question, you can do something like this:
// get references to the buttons
const button1 = document.querySelector('.btn');
const button2 = document.querySelector('.btn2');
// declare a function that adds the class 'red' to items matching the given selector
const select = selector => {
[...document.querySelectorAll(selector)].forEach(
element => element.classList.add('red')
);
}
// declare a function that removes the given class from all elements that currently have it
const deselect = className => {
[...document.querySelectorAll('.' + className)].forEach(
element => element.classList.remove(className)
);
}
// add a click handler to the button that invokes the
// select function above for items whose class includes 'title'
button1.addEventListener('click', () => select('.title'));
// add a click handler to the second button that removes the 'red' class from all items
button2.addEventListener('click', () => deselect('red'));
.red {
color: red;
}
<ul>
<li class="title">John</li>
<li class="title">Jack</li>
<li class="title">Joe</li>
<li class="title1">Jim</li>
<li class="title">David</li>
<li class="title">Sam</li>
<li class="title1">Jay</li>
<li class="title">Frank</li>
<li class="title">Tim</li>
<li class="title">Zack</li>
<li class="title">Lewis</li>
<li class="title1">Danny</li>
</ul>
<button class="btn" data-color="red">Change 9 names to red</button>
<button class="btn2" data-color="black">Reset</button>
a more efficient solution
This may not suit your needs, but if you just want to change the color of title items you could toggle a class on the <ul> and apply a css rule:
// get references to the button and ul
const button = document.querySelector('.btn');
const ul = document.querySelector('ul');
// toggle a class on the ul
button.addEventListener('click', () => ul.classList.toggle('red'));
/*
color 'title' items when the
ul has the 'red' class
*/
ul.red .title {
color: red;
}
<ul>
<li class="title">John</li>
<li class="title">Jack</li>
<li class="title">Joe</li>
<li class="title1">Jim</li>
<li class="title">David</li>
<li class="title">Sam</li>
<li class="title1">Jay</li>
<li class="title">Frank</li>
<li class="title">Tim</li>
<li class="title">Zack</li>
<li class="title">Lewis</li>
<li class="title1">Danny</li>
</ul>
<button class="btn">Toggle 'title' items to red</button>
markup issues
id attributes must be unique within a document. if you need to attach the same identifier to multiple elements use class instead.
<button> cannot be a child of <ul>.

First, id values must be unique, so you should be using class to
organize the similar <li> elements and use id to uniquely
identify the two buttons.
Also, the only elements that can be a child of a <ul> are <li>,
<script> and <template> elements, not <button>, so the buttons
have to be moved out of the ul.
From there, it's just a matter of setting the two buttons click handlers to the same event handler that loops over the li elements with the given class (not the buttons as you are trying to do) and adds or removes a pre-made class to the list depending on which button was clicked.
// test.js contents
document.getElementById("btn").addEventListener("click", changeColor);
document.getElementById("btn2").addEventListener("click", changeColor);
let items = document.querySelectorAll(".title");
function changeColor(event){
items.forEach(function(item){
// Figure out which button got us here
if(event.target.id === "btn"){
item.classList.add("red"); // Add red
} else {
item.classList.remove("red"); // Remove red
}
});
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Change the Certain Font Color with JavaScript</title>
<style>
.red {color:red;}
</style>
</head>
<body>
<ul>
<li class="title">John</li>
<li class="title">Jack</li>
<li class="title">Joe</li>
<li class="title1">Jim</li>
<li class="title">David</li>
<li class="title">Sam</li>
<li class="title1">Jay</li>
<li class="title">Frank</li>
<li class="title">Tim</li>
<li class="title">Zack</li>
<li class="title">Lewis</li>
<li class="title1">Danny</li>
</ul>
<button id="btn">Change 9 names to red</button>
<button id="btn2">Reset</button>
<script src="test.js"></script>
</body>
</html>
Additional notes:
You'll want to stay away from using .getElementsByClassName().
Rather than looping with counter indexes, it's much simpler to use
the Array.forEach() method on the collection returned from .querySelectorAll().
You should avoid using inline styles whenever possible as they are the hardest to override and to maintain. Instead, add, remove, or toggle the use of CSS classes with the .classList API, which is much simpler to use.

Related

JavaScript function only working on elements that have not already been manipulated by other functions

I wanted to make a list of elements that has the functionality that if you click a list element its text turns red. I then wanted to make a button that would set all the elements in the list to black. However, the button only makes the list elements that have not been clicked, to black. The newly red items are unaffected.
<ul id="outerUl" style="color:pink">
<li>First item in list</li>
<li>Second item in list</li>
</ul>
<input type="submit" onclick="clearListColour()">
And the JavaScript:
<script>
var textColourChanger = function(e){
e.target.style.color = 'red';
}
function clearListColour(){
document.getElementById('outerUl').style.color = 'black';
}
document.getElementById('outerUl').addEventListener('click', textColourChanger);
When you hit the button, you can see in inspect that the color of the ul is set to black, but it doesn't seem to be able to make it in an li that has had it colour set by clicking.
The button you created sets 'color' attribute in <ul> element, so it will affect every element that is inside of <ul>
To overwrite the color from <ul> you need to set the color directly in <li> element
To achieve the effect of changing the color of only clicked <li> item, you would need to addEventListener to specific <li> items, so whenever one of them is clicked, its font color changes. To reset them all to black you'd have to do the same, change all of their font colors back to black, or manipulate css with !important flag on <ul> color.
Add ids to your <li> items like so <li id="li1"> <li id="li2">
addEventListeners to them by Id and you should be good to go
more easy if you use some css:
const
outerUl = document.getElementById('outerUl')
, BtClear = document.getElementById('bt-clearList')
;
outerUl.onclick = e =>
{
if (!e.target.matches('li')) return
e.target.classList.add('onRed')
}
BtClear.onclick = e =>
{
outerUl.querySelectorAll('li.onRed').forEach(LI=>LI.classList.remove('onRed'))
}
#outerUl li {
color : pink;
cursor : pointer;
}
#outerUl li.onRed {
color : red;
}
<ul id="outerUl">
<li>First item in list</li>
<li>Second item in list</li>
</ul>
<button id="bt-clearList"> Clear List Colours</button>

Toggle class between list elements on click of a button

I need to remove and add a class between elements of a list, if I hit the #swapThumb button it should remove the selected class from the current element and then added to the next element.
Here's what I have
html
<ul id="product-thumbnails" class="thumbnails list-inline">
<li class="vtmb vt-123 selected" style="">
<img src="https://via.placeholder.com/100x100">
</li>
<li class="vtmb vt-456" style="">
<img src="https://via.placeholder.com/100x100">
</li>
<li class="vtmb vt-789" style="display: none">
<img src="https://via.placeholder.com/100x100">
</li>
<li class="vtmb vt-101" style="">
<img src="https://via.placeholder.com/100x100">
</li>
<li class="vtmb vt-121" style="display: none">
<img src="https://via.placeholder.com/100x100">
</li>
</ul>
<button id="swapThumb">Next</button>
javascript
let thumbsNailsList = $('#product-thumbnails').find('li');
let swapButton = $('#swapThumb');
thumbsNailsList.each((index, item) => {
let thumbsAvailable = $(item).attr('style');
if (thumbsAvailable === '') {
$(swapButton).on('click', () => {
$(item).removeClass('selected');
$(item).closest($(item)).next().addClass('selected');
});
}
});
First I'm checking if the li element has an empty style attribute (this is needed), if so, trigger the click validation.
The click should remove the selected class from the first element and then added to the next one and so on (it should match the empty style attribute). Once the selected class hits the last element of the list it should return the class to the first element.
This code snippet will change the class of the element beneath it to selected and remove it from the current one, while keeping all the other classes. It will also loop back to the beginning element if next is clicked when on the last element. I've heard jQuery functions are more expensive that document functions and shouldn't be used for these kinds of things. Apply this to your problem and you should get the expected result
let i = 0;
let thumbsNailsList = document.getElementById("product-thumbnails").children;
let btn = document.getElementById("btn");
btn.onclick = function() {
var prevClasses = thumbsNailsList[i].className;
thumbsNailsList[i].className = prevClasses.replace("selected", "");
i = (i+1) % thumbsNailsList.length;
thumbsNailsList[i].className = "selected";
console.log(thumbsNailsList);
}
<ul id="product-thumbnails">
<li class='selected'></li>
<li class=''></li>
<li class=''></li>
<li class=''></li>
</ul>
<button id="btn">Next</button>

How to use jQuery toggle method dynamically?

In my project, there are so many jQuery toggles needed for changing text and icons. Now I’m doing that using:
$("#id1").click(function () {
//Code to toggle display and change icon and text
});
$("#id2").click(function () {
//Same Code to toggle display and change icon and text as above except change in id
});
The problem is that I got so many to toggle, the code is quite long but all I change for each one is the id. So I was wondering if there is any way to make this simple.
Below is a sample pic. I got so many more in single page.
There are two issues here.
How to run the same action on multiple elements
How to know which element you've clicked so that you can run a relevant action on it. (most of the existing answers skip this part).
The first is to use a class for each of the elements you want to click, rather than wire up via an id. You can use a selector similar to [id^=id] but it's just cleaner to use a class.
<div id="id1" class="toggler">...
which allows you to:
$(".toggler").click(function() ...
the second is it associate the clickable with the item you want to toggle. There are many ways to do this, my preferred option is to associate them with data- attributes, eg:
<div class="togger" data-toggle="#toggle1">...
which allows you to:
$(".toggler").click(function() {
$($(this).data("toggle")).toggle();
});
The key here is that this is the element being clicked, so you can do anything else with this such as show/hide an icon inside or change colour.
Example:
$(".toggler").click(function() {
$($(this).data("toggle")).toggle();
$(this).toggleClass("toggled");
});
.toggler { cursor: pointer }
.toggled { background-color: green }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="toggler" data-toggle="#t1">T1</div>
<div class="toggler" data-toggle="#t2">T2</div>
<div class="toggler" data-toggle="#t3">T3</div>
<hr/>
<div id="t1" style='display:none;'>T1 content</div>
<div id="t2" style='display:none;'>T2 content</div>
<div id="t3" style='display:none;'>T3 content</div>
Oh,Can you use a class instead of id?
<ul>
<li class="idx">A</li>
<li class="idx">B</li>
<li class="idx">C</li>
</ul>
$(".idx").click(function(e){
//Code to toggle display and change icon and text
let target = e.target;
//You can do all what you want just base on the `target`;
});
You can store the queries in an array, and iterate over them to perform the same JQuery operation on all of them
let ids = ["#id1", "#id2", "#id3", "#randomID"]
ids.forEach((id) => {
console.log($(id).html())
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li id="id1">A</li>
<li id="id2">B</li>
<li id="id3">C</li>
<li id="randomID">D</li>
</ul>
Or (If like your example) and all of the id's are actually id1, id2, id3, ... etc.
let id = "id";
let n = 3; //amount of id's
for (let i = 1; i <= n; i++) {
console.log($("#" + id + i).html())
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li id="id1">A</li>
<li id="id2">B</li>
<li id="id3">C</li>
</ul>
You can try the below code.
var num = $("#myList").find("li").length;
console.log(num)
for(i=0;i<num;i++){
$("#id"+ i).click(function(e){
let target = e.target;
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<ul id="myList">
<li id="id1">A</li>
<li id="id2">B</li>
<li id="id3">C</li>
</ul>

Changing class of all li elements javascript/jquery button

I am working on a site with an accordion style script i've found. I'm quite new to jquery/javascript, so please bear with me...
My basic page opens an accordion, where the first one is locked open, and the other ones opens/closes if pressed. What keeps my top panel locked open is a
<li class="locked">.
The other panels have only
<li>
no class. I guess I can call them
<li class="somethingelse">
I want a button in my top panel that changes all the
to
<li class="locked">, so that the user can view the entire site.
I have tried:
<div>
<ul>
<li class="locked">Something
<button onclick="myFunction()">Click me</button>
</li>
<li id="abcd" class="somethingelse">somethingelse</li>
<li id="abcd"class="somethingelse">somethingelse</li>
</ul>
</div>
<script>
function myFunction() {
document.getElementById("abcd").class = "locked";
}
</script>
In plain JavaScript, I'd suggest:
function myFunction() {
// retrieves a NodeList of all <li> elements that do not
// have the 'locked' class-name:
var liElements = document.querySelectorAll('li:not(.locked)');
// uses Array.prototype.forEach to iterate over the array-like
// NodeList:
Array.prototype.forEach.call(liElements, function (li, index, list) {
// first argument ('li' ): the current array-element,
// second argument ('index'): unused, the index of the current
// array-element in the array,
// third argument ('list'): the array itself
// adding the 'locked' class-name to the list of classes
// of the current node:
li.classList.add('locked');
});
}
li {
opacity: 0.3;
}
li.locked {
opacity: 1;
}
<div>
<ul>
<li class="locked">Something
<button onclick="myFunction()">Click me</button>
</li>
<li class="somethingelse">somethingelse</li>
<li class="somethingelse">somethingelse</li>
</ul>
</div>
<script>
function myFunction() {
var liElements = document.querySelectorAll('li:not(.locked)');
Array.prototype.forEach.call(liElements, function (li, index, list) {
li.classList.add('locked');
});
}
</script>
Further, I'd suggest binding the event-handling in JavaScript, rather than using in-line HTML attributes (onclick, etc), which makes for easier long-term maintenance (since everything's updated in the same place, and you don't have to remember where all the event-handling was assigned):
function myFunction() {
var liElements = document.querySelectorAll('li:not(.locked)');
Array.prototype.forEach.call(liElements, function(li, index, list) {
li.classList.add('locked');
});
}
// finding the first element that matches the CSS selector.
// adding the named function (myFunction) as a 'click'
// event-handler:
document.querySelector('li.locked button').addEventListener('click', myFunction);
li {
opacity: 0.3;
}
li.locked {
opacity: 1;
}
<div>
<ul>
<li class="locked">Something
<button onclick="myFunction()">Click me</button>
</li>
<li class="somethingelse">somethingelse</li>
<li class="somethingelse">somethingelse</li>
</ul>
</div>
References:
CSS:
Negation (:not()) pseudo-class.
JavaScript:
document.querySelector().
document.querySelectorAll().
EventTarget.addEventListener().
Element.classList.
You should be changing element class as below
document.getElementById("abcd").className = "locked";
If you want to select more then one element use class name:
var nodes = document.getElementsByClassName("somethingelse");
So your final function will look like:
function myFunction() {
var nodes = document.getElementsByClassName("somethingelse");
var arr = Array.prototype.slice.call(nodes);
arr.forEach( function(node) {
node.className = "locked";
});
}
li {
color: #000000;
}
li.locked {
color: #ff0000;
}
<div>
<ul>
<li class="locked">Something
<button onclick="myFunction()">Click me</button>
</li>
<li id="abcd" class="somethingelse">somethingelse</li>
<li id="abcd" class="somethingelse">somethingelse</li>
</ul>
</div>

adding extra identifiers to dom elements

I have a bunch of menu items in a list format like so
<ul class="menu unselectable">
<li class="group">
Group Title
<ul>
<li class="groupItem i0">item 0</li>
<li class="groupItem i1 over">item 1</li>
</ul>
</li>
<li class="group">
Another Group Title
<ul>
<li class="groupItem i2">item 2</li>
<li class="groupItem i1">item 1 (if I hover here, the others should too</li>
</ul>
</li>
</ul>
The idea is, if I hover on one item with class i1 then all i1 items should behave the same. So I thought of adding a class over to all i1 items when I hover on any of them like so.
$(".groupItem").hover(
function () {
$(this).addClass("over");
},
function () {
$(this).removeClass("over");
}
);
The problem is I can't think of a way to identify what item has just been hovered on aside from $(this). To remedy this I thought of adding i1 as an id to items, but different dom nodes shouldn't have the same id. My next idea was to add the attribute value to the li items but to no avail (when I did a quick test with $(this).val() kept returning 0 regardless of the value actually stored in the node.
Is there any way I can add an identifier so I can just say $(this).<someIdentifier> , and target all the dom nodes with that identifier?
you can add an attribute groupID="{id}" and then call $(this).attr('groupID')
Element.prototype.secondId = '';
and than
document.getElementById('id5').secondId = 13;
As this you just set on any element a new property which you can use as you wish but is just in javascript not in html.
I don't recommend adding false attributes to elements, and this will work even if data attributes are not well supported by the user's browser:
$(".groupItem").hover(
function () {
var className = this.className.split(' ')[1];
$('.' + className).addClass("over");
},
function () {
var className = this.className.split(' ')[1];
$('.' + className).removeClass("over");
}
);
NOTE: Requires that classes are always organized as you specified above. A safer way could be:
var className = $.trim(this.className.replace('groupItem',''));
$(this).filter('#selector')
Please, Try working below code as below once:
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" type="text/javascript"></script>
<style>
.menu{ display:inline;}
.menu li{ display:inline; float: left;width: 100px;}
.menu li ul{display:none;}
</style>
<script type="text/javascript">
$(document).ready(function(){
$(".group").hover(
function () {
$(this).find("ul").show();
},
function () {
$(this).find("ul").hide();
}
);
});
</script>
</head>
<body>
<ul class="menu">
<li class="group">
Group Title
<ul>
<li>GT 1</li>
<li>GT 2</li>
</ul>
</li>
<li class="group">
Trochlear Nerve
<ul>
<li>TN 1</li>
<li>TN 2</li>
</ul>
</li>
</ul>
</body>
</html>

Categories