<ul id="List">
<li class="li">1</li>
<li class="li">2</li>
</ul>
<ul id="List2"></ul>
const items = document.querySelectorAll(".li");
for(var i = 0; i < items.length; i++){
items[i].onclick = function(){
const list = document.getElementById("List2");
list.insertBefore(items[i], list.childNodes[0]);
}
}
im trying to move the clicked li element to another ul with the insertBefore method but it doesnt do anything when i click on one of the li elements, how can i do this? or am i doing anything wrong? Thanks in advance :D
Pure JS solution EDIT: The second solution is the more correct one
You can use append like :
const listone = document.querySelector("#List");
const listwo = document.querySelector("#List2");
var li = listone.querySelectorAll("li");
for (var i = 0; i < li.length; i++) {
li[i].onclick = function() {
listwo.append(this);
}
}
function MoveLi(el){
}
#List li{
color:red;
}
#List2 li{
color:blue;
}
<ul id="List">
<li>1</li>
<li>2</li>
</ul>
<ul id="List2"></ul>
After some tips in the comments, addEventListener solution:
const listone = document.querySelector("#List");
const listwo = document.querySelector("#List2");
const li = listone.querySelectorAll("li");
function MoveLi(){
listwo.append(this);
this.removeEventListener("click", MoveLi);
}
li.forEach( (el) => {
el.addEventListener("click", MoveLi);
});
#List li{
color:red;
}
#List2 li{
color:blue;
}
<ul id="List">
<li>1</li>
<li>2</li>
</ul>
<ul id="List2"></ul>
As you've tagged jQuery in the question, this can be achieved by using appendTo(). As you've only got 2 ul elements in the DOM the logic is simply to append the clicked li to the ul which is not its parent. Try this:
let $uls = $('#List, #List2');
$('li').on('click', e => {
let $li = $(e.target);
$li.appendTo($uls.not($li.closest('ul')));
});
/* Just to make the demo clearer: */
ul { border: 1px solid #C00; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul id="List">
<li class="li">1</li>
<li class="li">2</li>
<li class="li">3</li>
<li class="li">4</li>
<li class="li">5</li>
</ul>
<ul id="List2"></ul>
Assign the click handler to the original UL element - otherwise your LI elements will swap positions even after appended to the target UL (List2)
Use Event.target.closest("li") to retrieve the LI element
Finally, use Element.append()
const EL_list = document.querySelector("#List");
const EL_list2 = document.querySelector("#List2");
EL_list.addEventListener("click", (ev) => {
const EL_LI = ev.target.closest("li");
EL_list2.append(EL_LI);
});
#List2{background:gold;}
<ul id="List">
<li class="li">1</li>
<li class="li">2</li>
</ul>
<ul id="List2"></ul>
Tip: Never use onclick unless you create brand new Elements from in-memory. Use the better additive method Element.addEventListener() instead
Related
<button>Click!</button>
<ul>
<li class="green">Home</li>
<li class="green">faq</li>
<li class="green">dropdown</li>
<li class="green">about</li>
<li class="green">contact</li>
</ul>
let li = document.querySelectorAll('li');
let btn = document.querySelector('button');
for(let i=0; i<li.length; i++) {
let number = 0;
btn.addEventListener('click', ()=>{
li[number++].classList.add('red')
if(number === li.length) {
number = 0
}
})
}
I wanted a single item to turn red with each click and the previous one to turn green again
You can do it like this if this helps you. As soon as you press the button. Remove the red class from every element first and then add it like you were doing
let li = document.querySelectorAll('li');
let btn = document.querySelector('button');
for(let i=0; i<li.length; i++) {
let number = 0;
btn.addEventListener('click', ()=>{
for(let i=0; i<li.length; i++) {
li[i].classList.remove('red')
}
li[number++].classList.add('red')
if(number === li.length) {
number = 0
}
})
}
.green {
color:green;
}
.red {
color:red;
}
<button>Click!</button>
<ul>
<li class="green">Home</li>
<li class="green">faq</li>
<li class="green">dropdown</li>
<li class="green">about</li>
<li class="green">contact</li>
</ul>
Doing this with JavaScript and one event listener
document.querySelector(".menu").addEventListener("click", function (e) {
// find the li that was clicked
const clickedLi = e.target.closest("li");
if (!clickedLi) return;
// see if we had something clicked already
const selected = e.currentTarget.querySelector(".red");
if (selected) selected.classList.remove('red');
// update the class on what was clicked
clickedLi.classList.add('red');
});
.green {
background-color: green;
}
li.red {
background-color: red;
}
<ul class="menu">
<li class="green">Home</li>
<li class="green">faq</li>
<li class="green">dropdown</li>
<li class="green">about</li>
<li class="green">contact</li>
</ul>
Using just html and css to make the selections
.menu input[type="radio"] {
display: none;
}
.menu input[type="radio"]+label {
display: inline-block;
width: 100%;
background-color: green;
}
.menu input[type="radio"]:checked + label {
background-color: red;
}
<ul class="menu">
<li><input type="radio" name="list" id="li1"><label for="li1">Foo</label></li>
<li><input type="radio" name="list" id="li2"><label for="li2">Bar</label></li>
<li><input type="radio" name="list" id="li3"><label for="li3">Baz</label></li>
<li><input type="radio" name="list" id="li4"><label for="li4">Cheese</label></li>
</ul>
Actually, I'm learning DOM Manipulation. As you can see, I created the li element but the issue is it is not applying the fontSize to the li.
const title = document.querySelector("#main-heading");
title.style.color = "red";
const listItems = document.querySelectorAll(".list-items");
for (i = 0; i < listItems.length; i++) {
listItems[i].style.fontSize = "2rem";
}
const ul = document.querySelector("ul");
const li = document.createElement("li");
ul.append(li);
li.innerText = "X-men"
li.setAttribute('class' , 'list-items' )
<div class="container">
<h1 id="main-heading">Favourite Movie</h1>
<ul>
<li class="list-items">The Matric</li>
<li class="list-items">Star Wars</li>
<li class="list-items">Harry Potter</li>
<li class="list-items">Lord of the Rings</li>
<li class="list-items">Marvel</li>
</ul>
</div>
The order you do things matters.
You find all the items matching .list-items
You change their font size
You create a new list item that matches .list-items
The item you create at step 3 didn't exist when you did the search at step 1 so wasn't changed by step 2.
Use a style sheet instead of inline style (which is what you modify with ...style.fontSize.
Remove setting the font-size value inside for loop. Instead have it as separate css.
.list-items {
font-size: 2rem;
}
Append the newly created li after the class is added.
ul.append(li);
Forked Example:
const title = document.querySelector("#main-heading");
title.style.color = "red";
const listItems = document.querySelectorAll(".list-items");
const ul = document.querySelector("ul");
const li = document.createElement("li");
li.innerText = "X-men"
li.setAttribute('class' , 'list-items');
ul.append(li);
.list-items {
font-size: 2rem;
}
<div class="container">
<h1 id="main-heading">Favourite Movie</h1>
<ul>
<li class="list-items">The Matric</li>
<li class="list-items">Star Wars</li>
<li class="list-items">Harry Potter</li>
<li class="list-items">Lord of the Rings</li>
<li class="list-items">Marvel</li>
</ul>
</div>
I tried to add "active class" which will change the color of the navigation item (displayed through li tags) when user clicks on it. To do this, I make a function to remove active class if there is any in all li elements. After that, when there is a click on navi item, I will add the active class to that element.
The problem is that when running my code, instead of just one item has "active" class, all items have.
I found many solutions for this problem, but most of them use jQuery which I have no knowledge about the library.
I hope someone can point my code errors below.
Thank you!
// Find all li tags
const liTags = document.querySelectorAll('li');
// Function to remove the current element has active class
function RemoveActive() {
for (let i = 0; i < liTags.length; i++) {
const currentActiveClass = document.querySelector('.active');
// Remove active class in the current li element
if (currentActiveClass != null) {
liTags[i].classList.remove('active');
}
}
}
// Add the active class to the clicked item
for (let i = 0; i < liTags.length; i++) {
liTags[i].addEventListener('click', function() {
RemoveActive;
liTags[i].classList.add('active');
})
}
As you can see from my example, i add classList.contains instead of check element then you have a typo error () into function
// Find all li tags
const liTags = document.querySelectorAll('li');
function RemoveActive() {
for (let i = 0; i < liTags.length; i++) {
if (liTags[i].classList.contains('active')) {
liTags[i].classList.remove('active');
}
}
}
for (let i = 0; i < liTags.length; i++) {
liTags[i].addEventListener('click', function() {
RemoveActive();
liTags[i].classList.add('active');
})
}
.active{
background-color:red;
}
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
Instead of use remove and add you can use toggle like:
// Find all li tags
const liTags = document.querySelectorAll('li');
function RemoveActive() {
const li = document.querySelector('li.active')
if (li) {
li.classList.toggle("active");
}
}
for (let i = 0; i < liTags.length; i++) {
liTags[i].addEventListener('click', function() {
RemoveActive();
liTags[i].classList.toggle('active');
})
}
.active {
background-color: red;
}
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
I believe your error was RemoveActive; where it should have been RemoveActive(), but thought I'd take the time to refactor the code.
I would advise using camel case for the function names removeActive() and name them a more descriptive name than "RemoveActive" as it will make it easier for future development / to understand what this function does as the program grows.
const navigationItems = document.querySelectorAll('li');
function toggleActiveNavItem() {
navigationItems.forEach(item => {
item.addEventListener("click", function() {
addClickEventToNavigation(item)
}
}
}
function addClickEventToNavigation(item) {
// Remove active from every navigation item
navigationItems.forEach(individualNavigationItem =>{
// Other than the one passed to the function as having been clicked
if (individualNavigationItem != item) {
individualNavigationItem.classList.remove("active");
}
// If the clicked item does not have the active class, add it
if (!item.classList.contains("active")) {
individualNavigationItem.classList.add("active");
}
});
}
you should follow this practice for clean and less code
var root = document.querySelector(".root")
root.addEventListener("click",e=>{
var t = e.target,
li = t.closest("li")
if(li){
root.querySelectorAll("li").forEach(each=>each.classList.remove("active"))
li.classList.add("active")
}
})
.active {
background:blue;
color:white;
}
.root li {
padding:2px;
cursor:pointer;
}
<ul class="root">
<li class="active">items</li>
<li>items</li>
<li>items</li>
<li>items</li>
<li>items</li>
<li>items</li>
<li>items</li>
</ul>
From the above direct comment on the OP's question ...
"Regardless of whatever caused the malfunction rethink the entire approach. Right now every LI element available downwards the document level at query time features its own click handling. And even though one can do that (instead of event delegation) a much bigger question arises. Is there just one un/ordered list in the entire document? If not, be aware that the current approach will work across the active states of different lists ... means, any item click from within a random list removes the active class name (if it exists) of any other list item from other lists as well."
The next following example is for demonstration purposes only in order to show what can be achieved with an event delegation based approach ...
function getNestedListRoot(listNode) {
let elmNode = listNode;
while (listNode = listNode?.parentNode.closest('ol, ul')) {
elmNode = listNode;
}
return elmNode;
}
function handleNestedListItemsActiveState({ target }) {
// the LI element closest to the `click` source.
const srcLiElm = target.closest('li');
// guard
if (srcLiElm === null) {
return;
}
// the LI element's un/ordered list parent.
const listNode = srcLiElm.parentNode;
// the top most un/ordered list node
// of a nested list structure.
const listRoot = getNestedListRoot(listNode);
listRoot
.querySelectorAll('li')
.forEach(elmNode =>
// de'active'ate every LI element
// within the nested list structure.
elmNode.classList.remove('active')
);
let liElm = srcLiElm;
while (liElm) {
// follow the path of directly nested
// LI elements from the current inner to the
// most outer LI element and 'active'ate each.
liElm.classList.add('active');
liElm = liElm.parentNode.closest('li');
}
}
function initialize() {
document
.querySelectorAll('ol, ul')
.forEach(elmNode =>
elmNode.addEventListener('click', handleNestedListItemsActiveState)
)
}
initialize();
body { margin: -2px 0 0 0; }
ol, ul { margin: 0 0 10px 0; }
ol ul, ul ol, ol ol, ul ul { margin: 0; }
li { margin: 0 0 0 -20px; font-size: 12px; }
li.active,
li.active li.active { color: #fc0; }
li.active li { color: initial; }
<ol>
<li class="active">
OL_A-a
<ul>
<li>
OL_A-a__UL_A-a
</li>
<li class="active">
OL_A-a__UL_A-b
<ol>
<li>
OL_A-a__UL_A-b__OL_B-a
</li>
<li>
OL_A-a__UL_A-b__OL_B-b
</li>
<li class="active">
OL_A-a__UL_A-b__OL_B-c
</li>
</ol>
</li>
<li>
OL_A-a__UL_A-c
</li>
</ul>
</li>
<li>
OL_A-b
</li>
<li>
OL_A-c
<ul>
<li>
OL_A-c__UL_B-a
</li>
<li>
OL_A-c__UL_B-b
</li>
<li>
OL_A-c__UL_B-c
</li>
</ul>
</li>
</ol>
<ol>
<li>
OL_A-a
<ul>
<li>
OL_A-a__UL_A-a
</li>
<li>
OL_A-a__UL_A-b
<ol>
<li>
OL_A-a__UL_A-b__OL_B-a
</li>
<li>
OL_A-a__UL_A-b__OL_B-b
</li>
<li>
OL_A-a__UL_A-b__OL_B-c
</li>
</ol>
</li>
<li>
OL_A-a__UL_A-c
</li>
</ul>
</li>
<li>
OL_A-b
</li>
<li class="active">
OL_A-c
<ul>
<li class="active">
OL_A-c__UL_B-a
</li>
<li>
OL_A-c__UL_B-b
</li>
<li>
OL_A-c__UL_B-c
</li>
</ul>
</li>
</ol>
http://jsfiddle.net/deepansh/BHCZ4/2/ this is a fiddle.
I want to save toggle state in cookie so that after page reload I get the same state, and I want to close previously-open list after clicking for opening new list.
I want to do in in minimum lines.
HTML
<ul class="nav sidebar-nav" id="am_menu">
<li> <span>User</span>
<ul>
<li>Add User
</li>
<li>List User
</li>
<li>User Profile
</li>
</ul>
</li>
<li> <span>User</span>
<ul>
<li>Add User
</li>
<li>List User
</li>
<li>User Profile
</li>
</ul>
</li>
</ul>
CSS:
ul {
width: 200px;
}
img {
width: 14px;
}
li ul {
display: none;
}
li ul {
padding-left: 4em;
list-style:none;
}
li ul li {
line-height:35px;
}
li ul li ul {
padding-left: .5em;
}
JS
$(function () {
$('li').filter(function (i) {
return $('ul', this).length >= 1;
}).each(function (i) {
$(this).children("a")
.click(function (e) {
var $ul = $(this).next("ul");
if ($ul.is(":visible")) {
$ul.find("ul").toggle("slow()");
$ul.toggle("slow()");
} else {
$ul.toggle("slow()");
};
})
});
});
I'd personally take the approach (using, as in Arun's answer, the $.cookie plugin):
$(function () {
var toShow = $.cookie('lastShownIndex'),
topLevel = $('#am_menu').find('> li');
topLevel.click(function(){
$(this).children('ul').slideToggle().end().siblings().children('ul').slideUp();
$.cookie('lastShownIndex', $(this).index());
}).eq(toShow).find('ul').show();
});
JS Fiddle demo.
References:
children().
click().
end().
eq().
find().
index().
show().
slideUp().
jQuery cookie plugin is used
$(function () {
$('#am_menu li:has(ul) > a').click(function (e) {
var $ul = $(this).next("ul");
$ul.toggle("slow");
$('#am_menu li ul').not($ul).slideUp();
$.cookie('curr.menu', $(this).parent().index())
});
var cindex = $.cookie('curr.menu');
if (cindex != undefined) {
$('#am_menu li:has(ul)').eq(cindex).children('a').click()
}
});
Demo: Fiddle
On my website I've got a sticky header with several different nav items on it that when clicked will scroll down to find that section on the page. I was wondering how one would go about setting it up so the nav items change colour when the view is on the section it corresponds to. In other words, if the viewer is on section 'x', 'x' on the nav bar will change color.
Update: heres the code for the nav bar im using
<div class = 'nav-container'>
<nav>
<div id = 'nav-items-container'>
<ul class='nav-items'>
<li class='nav-item'><a href='#what'>what</a></li>
<li class='nav-item'><a href='#how'>how</a></li>
<li class='nav-item'><a href='#why'>why</a></li>
<li class='nav-item'><a href='#who'>who</a></li>
<li class='nav-item'><a href='#where'>where</a></li>
</ul>
</div>
</nav>
</div>
some css
.nav-container{
background-color:black;
height:50px;
width:410px;
font-size: 120%;
position:absolute;
}
a:link{
color:white;
}
a:visited{
color:#58ACFA;
}
#nav-items-container ul li{
display:inline;
}
#nav-items-container ul li a{
padding: 20px;
text-decoration:none;
}
#nav-items-container ul{
margin:0;
padding:0;
list-style-type: none;
text-align: center;
padding-top:15px;
}
If you can use jquery you can do something like:
<script type="text/javascript">
$(function() {
var sections = [],
anchors = $('#nav-items-container a[href^="#"]'), // anchor links with hash tags
docHeight = $(document).height(),
currentOffset,
setNavActive;
// handler to update the class
setNavActive = function(hash){
anchors.removeClass('current-section');
anchors.filter('a[href="' + hash + '"]').addClass('current-section');
};
// building our hash/start/end position map
$.each(anchors, function(i, item) {
currentOffset = $(item.hash).offset().top;
if (i > 0) {
sections[i-1].end = currentOffset;
}
sections[i] = {
hash: item.hash,
start: (i == 0 ? 0 : currentOffset),
end: docHeight
};
});
// on scroll event, check which map fits,
// find the hash and set the class
$(document).scroll(function() {
currentOffset = $(document).scrollTop();
for (var i = 0; i < sections.length; i++) {
if (sections[i].start <= currentOffset && sections[i].end > currentOffset) {
setNavActive(sections[i].hash);
}
}
});
});
</script>
I added a new style but you can make it nested or whatever:
.current-section {background:pink; }
jsFiddle
http://jsfiddle.net/fstreamz/krb6Q/3/
There is not enough information here to give the best answer. I can give one that works though.
Chang your headers to look like this:
<li class='nav-item' id = "nav_what"><a href='#what'>what</a></li>
<li class='nav-item' id = "nav_how"><a href='#how'>how</a></li>
<li class='nav-item' id = "nav_why"><a href='#why'>why</a></li>
<li class='nav-item' id = "nav_who"><a href='#who'>who</a></li>
<li class='nav-item' id = "nav_where"><a href='#where'>where</a></li>
then in the body of each page put
<script>
document.getElementById('nav_what').style.backgroundColor = "gray";
</script>
You would have to switch it out on each page with the correct id. Its more traditionally done manually with inline styles if the header is not loaded externally.
Add another CSS declaration as below and apply active style to the current page.
#nav-items-container ul li.active a {
color:red;
}
Apply the above style like this...
<li class='nav-item active'><a href='#what'>what</a></li>
jsFiddle Demo