Javascript target only clicked element - javascript

So I have a side navigation that has a sub-menu in some of the list, and I'm trying to display the sub-menu only when it's clicked.
Here is the HTML
<div class="sidebar">
<ul class="nav">
<li class="main-menu" onclick="dispDrop()">Item 1
<ul class="sub-menu">
<li>Sub-item 1</li>
<li>Sub-item 1</li>
<li>Sub-item 1</li>
<li>Sub-item 1</li>
</ul>
</li>
<li>Item 2</li>
<li>Item 3</li>
<li class="main-menu" onclick="dispDrop()">Item 4
<ul class="sub-menu">
<li>Sub-item 1</li>
<li>Sub-item 1</li>
<li>Sub-item 1</li>
<li>Sub-item 1</li>
</ul>
</li>
<li>Item 5</li>
<li>Item 6</li>
</ul>
</div>
Here is its CSS
.nav ul {
display: none;
position: relative;
padding: 0px;
}
.nav li.active ul {
display: block;
}
Here is the javascript
<script type="text/javascript">
const navItem = document.querySelector('.main-menu');
function dispDrop() {
navItem.classList.toggle("active");
}
</script>
At first it was working fine, but when I added a submenu to another list, it started glitching and won't display the submenu.
Is there a way to target only the clicked <li> and only add/toggle the class to that clicked <li>?

What you can do is adding this to onclick="dispDrop()"
And then do the following.
function dispDrop(obj) {
obj.classList.toggle("active");
}
Demo
function dispDrop(obj) {
obj.classList.toggle("active");
}
.nav ul {
display: none;
position: relative;
padding: 0px;
}
.nav li.active ul {
display: block;
}
<div class="sidebar">
<ul class="nav">
<li class="main-menu" onclick="dispDrop(this)">Item 1
<ul class="sub-menu">
<li>Sub-item 1</li>
<li>Sub-item 1</li>
<li>Sub-item 1</li>
<li>Sub-item 1</li>
</ul>
</li>
<li>Item 2</li>
<li>Item 3</li>
<li class="main-menu" onclick="dispDrop(this)">Item 4
<ul class="sub-menu">
<li>Sub-item 1</li>
<li>Sub-item 1</li>
<li>Sub-item 1</li>
<li>Sub-item 1</li>
</ul>
</li>
<li>Item 5</li>
<li>Item 6</li>
</ul>
</div>

Some issues that I have Identified are listed below.
You are setting navItem as document.querySelector('.main-menu'). This will always returns the first DOM element with that class name. Not your required target.
Your click event will be triggering the click event of the anchor tag, that will result in reload of page.
I have fixed that by toggling the class list of the target elemet from the click tiggered. This will give the required target where the click is triggered.
Call e.preventDefault(); e.stopPropagation(); inside the click event to stop the click event triggering the anchor tag click event.
Working Fiddle
function dispDrop(e) {
e.currentTarget.classList.toggle("active");
e.preventDefault();
e.stopPropagation();
}
.nav ul {
display: none;
position: relative;
padding: 0px;
}
.nav li.active ul {
display: block;
}
<div class="sidebar">
<ul class="nav">
<li class="main-menu" onclick="dispDrop(event)">
Item 1
<ul class="sub-menu">
<li>Sub-item 11</li>
<li>Sub-item 11</li>
<li>Sub-item 11</li>
<li>Sub-item 11</li>
</ul>
</li>
<li>Item 2</li>
<li>Item 3</li>
<li class="main-menu" onclick="dispDrop(event)">
Item 4
<ul class="sub-menu">
<li>Sub-item 12</li>
<li>Sub-item 12</li>
<li>Sub-item 12</li>
<li>Sub-item 12</li>
</ul>
</li>
<li>Item 5</li>
<li>Item 6</li>
</ul>
</div>

I rewrite a little bit your code. The problem occurs from the anchor. You can
remove the anchor an style the li tag to have the anchor feeling (cursor: pointer; etc) or
you add a # to your href. that will avoid to follow the link
Then I pass with your onclick event the currecnt clickent object. Then you eventHandler knows which element was clicked.
function dispDrop(event) {
event.querySelector('ul').classList.toggle("hide");
}
.nav ul {
position: relative;
padding: 0px;
}
.hide {
display: none;
position: relative;
}
<div class="sidebar">
<ul class="nav">
<li class="main-menu" onclick="dispDrop(this)">
Item 1
<ul class="sub-menu hide">
<li>Sub-item 1</li>
<li>Sub-item 1</li>
<li>Sub-item 1</li>
<li>Sub-item 1</li>
</ul>
</li>
<li>Item 2</li>
<li>Item 3</li>
<li class="main-menu clicki" onclick="dispDrop(this)">
Item 4
<ul class="sub-menu hide">
<li>Sub-item A </li>
<li>Sub-item 1</li>
<li>Sub-item 1</li>
<li>Sub-item 1</li>
</ul>
</li>
<li>Item 5</li>
<li>Item 6</li>
</ul>
</div>

Related

How can I get the index of a list item element not "display:none;"

I want to find the index of a list item element that has no attribute style="display:none;".
Here is my basic list:
<ol>
<li style="display:none;">LI 1</li>
<li>LI 2</li>
<li style="display:none;">LI 3</li>
<li style="display:none;">LI 4</li>
<li style="display:none;">LI 5</li>
</ol>
Thank you for your answers :)
While you could search for an element without a style attribute
const li = document.querySelector('ol li:not([style])');
console.log(li.textContent);
<ol>
<li style="display:none;">LI 1</li>
<li>LI 2</li>
<li style="display:none;">LI 3</li>
<li style="display:none;">LI 4</li>
<li style="display:none;">LI 5</li>
</ol>
If you're going to select elements based on the style display, I'd highly recommend using a class instead:
const li = document.querySelector('ol li:not(.hide)');
console.log(li.textContent);
.hide {
display: none;
}
<ol>
<li class="hide">LI 1</li>
<li>LI 2</li>
<li class="hide">LI 3</li>
<li class="hide">LI 4</li>
<li class="hide">LI 5</li>
</ol>
I think you can do like this.
const li = document.querySelector('li:not([style="display:none;"])');
console.log(li.textContent);
<ol>
<li style="display:none;">LI 1</li>
<li style="display:block;">LI 2</li>
<li style="display:none;">LI 3</li>
<li style="display:none;">LI 4</li>
<li style="display:none;">LI 5</li>
</ol>

Select list item parent without children in nested lists in jQuery

I have this nested list:
<ul>
<li>Item 1
<ul>
<li>Item 1A</li>
<li>Item 1B</li>
<li>Item 1C</li>
</ul>
</li>
<li>Item 2
<ul>
<li>Item 2A</li>
<li class="active">Item 2B</li>
<li>Item 2C</li>
</ul>
</li>
<li>Item 3
<ul>
<li>Item 3A</li>
<li>Item 3B</li>
<li>Item 3C</li>
</ul>
</li>
</ul>
What I need is: When I hover on a list item with class="active", this specific item 'Item 2B' together with its parent 'Item 2' to be colored red. 'Item 2A' and 'Item 2C' should NOT be colored.
I tried closest():
$("li").on("mouseenter", function () {
if ($(this).hasClass("active")) {
$(this).css("color", "red");
$(this).closest("ul").closest("li").css("color", "red");
}
});
Also parent().parent():
$(this).parent().parent().css("color", "red");
but of course they both color the whole 'Item 2' list item including all children 2A, 2B and 2C
You have to wrap each of the parent element into some html element like div or span.
Solution
HTML
<ul>
<li>Item 1
<ul>
<li>Item 1A</li>
<li>Item 1B</li>
<li>Item 1C</li>
</ul>
</li>
<li><span>Item 2</span>
<ul>
<li>Item 2A</li>
<li class="active">Item 2B</li>
<li>Item 2C</li>
</ul>
</li>
<li>Item 3
<ul>
<li>Item 3A</li>
<li>Item 3B</li>
<li>Item 3C</li>
</ul>
</li>
</ul>
JS
$("li").on("mouseenter", function () {
if ($(this).hasClass("active")) {
$(this).css("color", "red");
$(this).parent().parent().find("span").css("color", "red");
}
});
For this to work you will need to wrap the Item 2 text in a containing element, such as a span, otherwise the colour of all child elements will be changed too.
Then you can use .parent.closest('li') to get the li, before using children() to retrieve that span. Try this:
$("ul").on("mouseenter mouseleave", 'li.active', function(e) {
var $target = $(this).parent().closest('li').children('span');
$target.add(this).toggleClass('hover', e.type == 'mouseenter');
});
.active {
font-weight: bold;
}
.hover {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>
<span>Item 1</span>
<ul>
<li>Item 1A</li>
<li>Item 1B</li>
<li>Item 1C</li>
</ul>
</li>
<li>
<span>Item 2</span>
<ul>
<li>Item 2A</li>
<li class="active">Item 2B</li>
<li>Item 2C</li>
</ul>
</li>
<li>
<span>Item 3</span>
<ul>
<li>Item 3A</li>
<li>Item 3B</li>
<li>Item 3C</li>
</ul>
</li>
</ul>
Note that the parent() call is required before calling closest() as closest() will match the current li element, which we do not want, so parent() is required to 'hop' over that element.
I'd probably do it by:
Using a standard :hover rule for the li itself
Using a class, not direct styling, for its parent li
Re-asserting the color for the subordinate ul elements (and thus their children) under the li with the class in the CSS
Like this:
$("li")
.on("mouseenter", function() {
if ($(this).hasClass("active")) {
$(this).parent().closest("li").addClass("has-active");
}
})
.on("mouseleave", function() {
if ($(this).hasClass("active")) {
$(this).parent().closest("li").removeClass("has-active");
}
});
li {
color: black;
}
li.has-active {
color: red;
}
li.active:hover {
color: red;
}
.has-active ul {
color: black;
}
<ul>
<li>Item 1
<ul>
<li>Item 1A</li>
<li>Item 1B</li>
<li>Item 1C</li>
</ul>
</li>
<li>Item 2
<ul>
<li>Item 2A</li>
<li class="active">Item 2B</li>
<li>Item 2C</li>
</ul>
</li>
<li>Item 3
<ul>
<li>Item 3A</li>
<li>Item 3B</li>
<li>Item 3C</li>
</ul>
</li>
</ul>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Why is my decision tree not working?

I am a beginner to coding, especially in HTML.
I have used the basic layout of this code that I found online (I can't seem to remember where), but I wanted a similar structure to this, and wanted to populate it with my own data.
I tried to put all the code from separate files, .js and .css part into the .html code. (I searched online how to do this, also I wasn't sure about jQuery, and found on https://www.w3schools.com/jquery/jquery_get_started.asp that I can add it into the <head> section instead of downloading the library? The code below doesn't use the jQuery, but I have no clue how to use it. Can this be inserted at the top of the .html file within the <script> tag?
Please can anyone tell me why this only shows up on my browser as three bullet points only, and when I hover over it, the mouse changes from arrow to pointer, but doesn't click/expand.
Any help will be much appreciated!
var tree = document.querySelectorAll('ul.tree a:not(:last-child)');
for (var i = 0; i < tree.length; i++) {
tree[i].addEventListener('click', function(e) {
var parent = e.target.parentElement;
var classList = parent.classList;
if (classList.contains("open")) {
classList.remove('open');
var opensubs = parent.querySelectorAll(':scope .open');
for (var i = 0; i < opensubs.length; i++) {
opensubs[i].classList.remove('open');
}
} else {
classList.add('open');
}
});
}
body {
font-family: Arial;
}
ul.tree li {
list-style-type: none;
position: relative;
}
ul.tree li ul {
display: none;
}
ul.tree li.open>ul {
display: block;
}
ul.tree li a {
color: black;
text-decoration: none;
}
ul.tree li a:before {
height: 1em;
padding: 0 .1em;
font-size: .8em;
display: block;
position: absolute;
left: -1.3em;
top: .2em;
}
ul.tree li>a:not(:last-child):before {
content: '+';
}
ul.tree li.open>a:not(:last-child):before {
content: '-';
}
<ul class="tree">
<li>Part 1
<ul>
<li>Item A
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item B
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item C
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item D
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item E
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
</ul>
</li>
<li>Part 2
<ul>
<li>Item A
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item B
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item C
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item D
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item E
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
</ul>
</li>
<li>Part 3
<ul>
<li>Item A
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item B
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item C
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item D
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item E
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
</ul>
</li>
</ul>
The :scope pseudo element isn't widely supported according to https://developer.mozilla.org/en-US/docs/Web/CSS/:scope but I'm not sure it was needed to acheive your goal. In fact, chrome and firefox worked fine for me and I just saw some javascript errors in IE and Edge - didn't test Safari.
This code is working for me in all of the tested browsers without javascript errors after simple removing :scope from the open sub element selector in the javascript.
var tree = document.querySelectorAll('ul.tree a:not(:last-child)');
for (var i = 0; i < tree.length; i++) {
tree[i].addEventListener('click', function(e) {
var parent = e.target.parentElement;
var classList = parent.classList;
if (classList.contains("open")) {
classList.remove('open');
var opensubs = parent.querySelectorAll('.open');
for (var i = 0; i < opensubs.length; i++) {
opensubs[i].classList.remove('open');
}
} else {
classList.add('open');
}
});
}
body {
font-family: Arial;
}
ul.tree li {
list-style-type: none;
position: relative;
}
ul.tree li ul {
display: none;
}
ul.tree li.open>ul {
display: block;
}
ul.tree li a {
color: black;
text-decoration: none;
}
ul.tree li a:before {
height: 1em;
padding: 0 .1em;
font-size: .8em;
display: block;
position: absolute;
left: -1.3em;
top: .2em;
}
ul.tree li>a:not(:last-child):before {
content: '+';
}
ul.tree li.open>a:not(:last-child):before {
content: '-';
}
<ul class="tree">
<li>Part 1
<ul>
<li>Item A
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item B
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item C
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item D
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item E
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
</ul>
</li>
<li>Part 2
<ul>
<li>Item A
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item B
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item C
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item D
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item E
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
</ul>
</li>
<li>Part 3
<ul>
<li>Item A
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item B
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item C
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item D
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item E
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
</ul>
</li>
</ul>

How to expand the nodes by default on page load

i have used the following code.Now i want to expand all the nodes by default on page load.can anyone suggest me how to do it
var tree = document.querySelectorAll('ul.tree a:not(:last-child)');
for(var i = 0; i < tree.length; i++){
tree[i].addEventListener('click', function(e) {
var parent = e.target.parentElement;
var classList = parent.classList;
if(classList.contains("open")) {
classList.remove('open');
var opensubs = parent.querySelectorAll(':scope .open');
for(var i = 0; i < opensubs.length; i++){
opensubs[i].classList.remove('open');
}
} else {
classList.add('open');
}
});
}
body {
font-family: Arial;
}
ul.tree li {
list-style-type: none;
position: relative;
}
ul.tree li ul {
display: none;
}
ul.tree li.open > ul {
display: block;
}
ul.tree li a {
color: black;
text-decoration: none;
}
ul.tree li a:before {
height: 1em;
padding:0 .1em;
font-size: .8em;
display: block;
position: absolute;
left: -1.3em;
top: .2em;
}
ul.tree li > a:not(:last-child):before {
content: '+';
}
ul.tree li.open > a:not(:last-child):before {
content: '-';
}
<ul class="tree">
<li >Part 1
<ul>
<li>Item A
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item B
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item C
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item D
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item E
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
</ul>
</li>
<li>Part 2
<ul>
<li>Item A
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item B
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item C
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item D
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item E
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
</ul>
</li>
<li>Part 3
<ul>
<li>Item A
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item B
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item C
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item D
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item E
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
</ul>
</li>
</ul>
Apply the class name of open to your list items <li> tag that you wish to expand by default. Simple find done by reading the source code which leads me to believe you don't really understand how the source code you're using works...
var tree = document.querySelectorAll('ul.tree a:not(:last-child)');
for (var i = 0; i < tree.length; i++) {
tree[i].addEventListener('click', function(e) {
var parent = e.target.parentElement;
var classList = parent.classList;
if (classList.contains("open")) {
classList.remove('open');
var opensubs = parent.querySelectorAll(':scope .open');
for (var i = 0; i < opensubs.length; i++) {
opensubs[i].classList.remove('open');
}
} else {
classList.add('open');
}
});
}
body {
font-family: Arial;
}
ul.tree li {
list-style-type: none;
position: relative;
}
ul.tree li ul {
display: none;
}
ul.tree li.open>ul {
display: block;
}
ul.tree li a {
color: black;
text-decoration: none;
}
ul.tree li a:before {
height: 1em;
padding: 0 .1em;
font-size: .8em;
display: block;
position: absolute;
left: -1.3em;
top: .2em;
}
ul.tree li>a:not(:last-child):before {
content: '+';
}
ul.tree li.open>a:not(:last-child):before {
content: '-';
}
<ul class="tree">
<li class="open">Part 1
<ul>
<li class="open">Item A
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li class="open">Item B
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li class="open">Item C
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li class="open">Item D
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li class="open">Item E
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
</ul>
</li>
<li class="open">Part 2
<ul>
<li class="open">Item A
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li class="open">Item B
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li class="open">Item C
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li class="open">Item D
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li class="open">Item E
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
</ul>
</li>
<li class="open">Part 3
<ul>
<li class="open">Item A
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li class="open">Item B
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li class="open">Item C
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li class="open">Item D
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li class="open">Item E
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
</ul>
</li>
</ul>
Update
As AVI has suggested: "you can also write something like tree[i].click(); before applying the event listener if you want that programmatically."
var tree = document.querySelectorAll('ul.tree a:not(:last-child)');
for (var i = 0; i < tree.length; i++) {
tree[i].addEventListener('click', function(e) {
var parent = e.target.parentElement;
var classList = parent.classList;
if (classList.contains("open")) {
classList.remove('open');
var opensubs = parent.querySelectorAll(':scope .open');
for (var i = 0; i < opensubs.length; i++) {
opensubs[i].classList.remove('open');
}
} else {
classList.add('open');
}
});
tree[i].click();
}

Not able to create a tree view with javascript with the code below

I'm trying to create a tree view with the help of a code that my friend sent me but it's not working in my browser. However, the code runs file on www.jsfiddle.com and doesn't seem to have any issues with it. Here's the link https://jsfiddle.net/te366hu2/2/
Below is how I'm trying to run this:
<html>
<head>
<title>Tree</title>
<style>
body {
font-family: Arial;
}
ul.tree li {
list-style-type: none;
position: relative;
}
ul.tree li ul {
display: none;
}
ul.tree li.open > ul {
display: block;
}
ul.tree li a {
color: black;
text-decoration: none;
}
ul.tree li a:before {
height: 1em;
padding:0 .1em;
font-size: .8em;
display: block;
position: absolute;
left: -1.3em;
top: .2em;
}
ul.tree li > a:not(:last-child):before {
content: '+';
}
ul.tree li.open > a:not(:last-child):before {
content: '-';
}
</style>
<script>
var tree = document.querySelectorAll('ul.tree a:not(:last-child)');
for(var i = 0; i < tree.length; i++){
tree[i].addEventListener('click', function(e) {
var parent = e.target.parentElement;
var classList = parent.classList;
if(classList.contains("open")) {
classList.remove('open');
var opensubs = parent.querySelectorAll(':scope .open');
for(var i = 0; i < opensubs.length; i++){
opensubs[i].classList.remove('open');
}
} else {
classList.add('open');
}
});
}
</script>
</head>
<body>
<ul class="tree">
<li>Part 1
<ul>
<li>Item A
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item B
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item C
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item D
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item E
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
</ul>
</li>
<li>Part 2
<ul>
<li>Item A
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item B
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item C
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item D
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item E
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
</ul>
</li>
<li>Part 3
<ul>
<li>Item A
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item B
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item C
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item D
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
<li>Item E
<ul>
<li>Sub-item 1</li>
<li>Sub-item 2</li>
<li>Sub-item 3</li>
</ul>
</li>
</ul>
</li>
</ul>
</body>
</html>
You're code seems to be correct; however, it's in the wrong spot. Move the entire script tags and their contents to the spot right before the closing body tag in your HTML.
<script>
var tree = document.querySelectorAll('ul.tree a:not(:last-child)');
for(var i = 0; i < tree.length; i++){
tree[i].addEventListener('click', function(e) {
var parent = e.target.parentElement;
var classList = parent.classList;
if(classList.contains("open")) {
classList.remove('open');
var opensubs = parent.querySelectorAll(':scope .open');
for(var i = 0; i < opensubs.length; i++){
opensubs[i].classList.remove('open');
}
} else {
classList.add('open');
}
});
}
</script>
</body>

Categories

Resources