How do I create a multilayer accordion menu? - javascript

I am trying to create a multilayer or nested accordion menu using pure JavaScript. The whole thing is functioning correctly expect for one issue: when I click on one element of the menu, the entire thing reacts.
This is not for a current project. I am simply trying to expand my knowledge after taking a basic course.
I know the easy-but-bloated way to fix this would be to replicate the functions three times and then specifically target each node (is that even the right word?) in the array. However, I image that would be the wrong way to do things. As you’ll see, I attempted to create a loop for multiple variables. I think this might be my issue because it seems to be the thing that is linking all of them together.
var accordion = document.getElementsByClassName("accordion");
var dropdown = document.getElementsByClassName("dropdown");
var accordionArrow = document.getElementsByClassName("accordionArrow");
var dropdownArrow = document.getElementsByClassName("dropdownArrow");
var content = document.getElementsByClassName("content");
function accordionFunction() {
for (j = 0, k = 0, l = 0, m = 0; j < dropdown.length, k < accordionArrow.length, l < dropdownArrow.length, m < content.length; j++, k++, l++, m++) {
if (dropdown[j].style.maxHeight) {
dropdown[j].style.maxHeight = null;
accordionArrow[k].style.transform = null;
content[m].style.maxHeight = null;
dropdownArrow[l].style.transform = null;
} else {
dropdown[j].style.maxHeight = dropdown[j].scrollHeight + "px";
accordionArrow[k].style.transform = "rotate(-135deg)";
}
}
};
for (var i = 0; i < accordion.length; i++) {
accordion[i].addEventListener("click", accordionFunction);
};
function accordionSubmenu() {
for (l = 0, m = 0; l < dropdown.length, m < content.length; l++, m++) {
if (content[m].style.maxHeight) {
content[m].style.maxHeight = null;
dropdownArrow[l].style.transform = null;
} else {
content[m].style.maxHeight = content[m].scrollHeight + "px";
dropdownArrow[l].style.transform = "rotate(-135deg)";
}
}
};
for (j = 0; j < dropdown.length; j++) {
dropdown[j].addEventListener("click", accordionSubmenu)
};
body {
margin: auto;
width: 600px;
}
div {
margin: auto;
}
.accordion {
background-color: lightblue;
color: white;
padding: 3%;
cursor: pointer;
width: 300px;
height: 50px;
}
.accordion .accordionArrow {
border: solid white;
border-width: 0 3px 3px 0;
display: inline-block;
padding: 3px;
transform: rotate(45deg);
}
.dropdown {
color: lightblue;
padding-left: 3%;
cursor: pointer;
width: 300px;
max-height: 0;
overflow: hidden;
transition-duration: 0.2s;
}
.dropdown .dropdownArrow {
border: solid lightblue;
border-width: 0 3px 3px 0;
display: inline-block;
padding: 3px;
transform: rotate(45deg);
}
.content {
font-weight: bold;
transition: all 0.2s ease;
padding-left: 5%;
max-height: 0;
overflow: hidden;
}
<div>
<h2 class="accordion">Main 1<i class="accordionArrow"></i></h2>
<h3 class="dropdown">Submenu 1<i class="dropdownArrow"></i></h3>
<p class="content">Hello there. We are exposed.</p>
</div>
<div>
<h2 class="accordion">Main 2<i class="accordionArrow"></i></h2>
<h3 class="dropdown">Submenu 1<i class="dropdownArrow"></i></h3>
<p class="content">Hello there. We are exposed again!</p>
</div>
<div>
<h2 class="accordion">Main 3<i class="accordionArrow"></i></h2>
<h3 class="dropdown">Submenu 1<i class="dropdownArrow"></i></h3>
<p class="content">Hello there. We are exposed thrice!</p>
</div>

With some smart use of the this keyword (the element that triggered the event handler) and the nextElementSibling property you can achieve this in a more elegant way.
I made a class to make elements max-height 0. So now i can hide and show elements by just adding or removing this class.
In the accordion function I toggle this class for the element after the clicked element and remove the class for all others.
In the accordion submenu I just toggle the class on the nextElementSibling.
Personally I would make a different html structure where you can toggle classes easier by using more levels of elements so you would only need 1 function for x amount of submenu's. Maybe a nice new challenge for you.
document.querySelectorAll('.accordion').forEach((accordion) => accordion.addEventListener('click', function() {
//Get All possible hidden elements and loop over it.
document.querySelectorAll('.dropdown, .content').forEach((collapsible) => {
//If current element is the same is the next sibling element of the event target toggle the class. Otherwise add it.
if(collapsible === this.nextElementSibling) {
collapsible.classList.toggle('maxHeightZero');
}
else {
collapsible.classList.add('maxHeightZero');
}
});
}));
document.querySelectorAll('.dropdown').forEach((dropdown) => dropdown.addEventListener('click', function() {
//toggle the nextElementSibling
this.nextElementSibling.classList.toggle('maxHeightZero');
}));
body {
margin: auto;
width: 600px;
}
div {
margin: auto;
}
.accordion {
background-color: lightblue;
color: white;
padding: 3%;
cursor: pointer;
width: 300px;
height: 50px;
}
.accordion .accordionArrow {
border: solid white;
border-width: 0 3px 3px 0;
display: inline-block;
padding: 3px;
transform: rotate(45deg);
}
.dropdown {
color: lightblue;
padding-left: 3%;
cursor: pointer;
width: 300px;
overflow: hidden;
transition-duration: 0.2s;
}
.dropdown .dropdownArrow {
border: solid lightblue;
border-width: 0 3px 3px 0;
display: inline-block;
padding: 3px;
transform: rotate(45deg);
}
.content {
font-weight: bold;
transition: all 0.2s ease;
padding-left: 5%;
overflow: hidden;
}
.maxHeightZero {
max-height: 0;
}
<div>
<h2 class="accordion">Main 1<i class="accordionArrow"></i></h2>
<h3 class="dropdown maxHeightZero">Submenu 1<i class="dropdownArrow"></i></h3>
<p class="content maxHeightZero">Hello there. We are exposed.</p>
</div>
<div>
<h2 class="accordion">Main 2<i class="accordionArrow"></i></h2>
<h3 class="dropdown maxHeightZero">Submenu 1<i class="dropdownArrow"></i></h3>
<p class="content maxHeightZero">Hello there. We are exposed again!</p>
</div>
<div>
<h2 class="accordion">Main 3<i class="accordionArrow"></i></h2>
<h3 class="dropdown maxHeightZero">Submenu 1<i class="dropdownArrow"></i></h3>
<p class="content maxHeightZero">Hello there. We are exposed thrice!</p>
</div>

Related

Dropdown with Javascript solution

Hello all I have a problem when I use two dropdowns, when you click on one, both are opened, below is my code:
function myFunction() {
document.getElementById("myDropdown").classList.toggle("show");
}
window.onclick = function (event) {
if (!event.target.matches('.dropbtn')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
function newFunction() {
document.getElementById("myDropdown1").classList.toggle("show");
}
window.onclick = function (event) {
if (!event.target.matches('.dropbtn')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
.dropbtn {
background-color: #ffffff;
color: #252930;
font-size: 18px;
border: none;
cursor: pointer;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropbtn-selected {
background-color: #ec292d;
color: #fff!important
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: max-content;
overflow: auto;
margin-top: 15px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 969999;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown a:hover {
background-color: #ddd;
}
.show {
display: block;
}
.dropbtn>i {
pointer-events: none;
}
<li class="dropdown desktopNav">
<button onclick="myFunction()" class="dropbtn">Test <i
class="fas fa-chevron-down"></i></button>
<div id="myDropdown" class="dropdown-content">
Lorem1
Lorem 2
Lorem 3
</div>
</li>
<li class="dropdown desktopNav">
<button onclick="newFunction()" class="dropbtn">Second <i
class="fas fa-chevron-down"></i></button>
<div id="myDropdown1" class="dropdown-content">
Lorem 4
Lorem 5
Lorem 6
</div>
</li>
Is there a way to make this code more flexible? I want to achieve, when the user clicks on the dropdown that dropdown should be only opened,.
Can somebody try to help me with this? What is the best way to achieve this?
To have only one dropdown visible at a time you need to hide all others. The simple way to achieve this is to group the element structures by classname based on their behaviour, as opposed to giving each block its own incremental id. The latter approach leads to bloated duplicate code which becomes harder to maintain.
To fix this remove the id and give all the elements in the repeated blocks the same class. From there you can use DOM traversal to relate the elements to each other, hiding and displaying as necessary.
let dropdownContents = document.querySelectorAll('.dropdown-content');
// display the dropdown relevant to the clicked button
document.querySelectorAll('.dropbtn').forEach(btn => {
btn.addEventListener('click', e => {
let targetContent = e.target.parentNode.querySelector(".dropdown-content");
targetContent.classList.toggle("show");
dropdownContents.forEach(el => el !== targetContent && el.classList.remove('show'));
});
});
// hide all dropdowns when the document is clicked
document.addEventListener('click', e => {
if (!e.target.matches('.dropbtn'))
dropdownContents.forEach(el => el.classList.remove('show'));
})
.dropbtn {
background-color: #ffffff;
color: #252930;
font-size: 18px;
border: none;
cursor: pointer;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropbtn-selected {
background-color: #ec292d;
color: #fff!important
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: max-content;
overflow: auto;
margin-top: 15px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 969999;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown a:hover {
background-color: #ddd;
}
.show {
display: block;
}
.dropbtn>i {
pointer-events: none;
}
<ul>
<li class="dropdown desktopNav">
<button class="dropbtn">Test <i class="fas fa-chevron-down"></i></button>
<div class="dropdown-content">
Lorem1
Lorem 2
Lorem 3
</div>
</li>
<li class="dropdown desktopNav">
<button class="dropbtn">Second <i class="fas fa-chevron-down"></i></button>
<div class="dropdown-content">
Lorem 4
Lorem 5
Lorem 6
</div>
</li>
</ul>
Note the use of addEventListener over the outdated onX properties, and also the click handler which hides the dropdowns being attached to the document instead of the window.
There is no seperate 'close' event, so when clicking the second, there's no reason why the first should close.
You could have a variable that is set when a dropdown is to be shown. When clicking the second dropdown, the function would check if the variable is set, and if so, toggle that one off before toggling the correct one on. Eg:
let dropdownShown = null;
function onClick(e) {
if (dropdownShown !== e.target) {
dropdownShown.classList.toggle("show");
}
e.target.classList.toggle("show");
dropdownShown = e.target;
}

removing a class when a search box is clicked

TLDR; The functionality I'm trying to create in this code example is upon clicking a box (collapsible has active class), and then you click on the search box, the active class is removed closing the collapsible. Currently, the remove class is not working. Any idea why?
In more detail:
I have a list of collapsible blocks get an active class when they are clicked. You can search a search box to filter out which block you want to be displayed, and thus click on it to display more content. I have realized that when you go back to search and didn't 'unclick' a box (to close the collapsible) the box is still active (which makes perfect sense).
I had an idea that upon search box focus, I would loop through all the collapsible and remove the active class itself (in turn, closing the collapsible).
I've gotten to the point where I can find which collapsible is active, but I cannot remove the active class.
Is this because the box was initially clicked to add the class and has to be physically clicked once again to remove it?
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.maxHeight) {
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
});
}
function searchClick() {
var searchTable;
searchTable = document.getElementById("search-table");
faqButtons = searchTable.getElementsByTagName("button");
for (i = 0; i < faqButtons.length; i++) {
if (faqButtons[i].classList.contains("active")) {
console.log('THIS ONE: ', faqButtons[i]);
faqButtons[i].classList.remove("active");
}
}
}
.collapsible {
display: flex;
justify-content: space-between;
background-color: white;
color: black;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 20px;
}
.collapsible span {
padding-left: 5px;
padding-right: 5px;
}
.collapsible .active,
.collapsible:hover {
background-color: #333;
color: white;
}
.content {
padding: 0 18px;
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
}
.content p {
margin-top: 10px;
}
<div class="search-faq">
<input autocomplete="off" type="text" id="search-faq" placeholder="Search for FAQ" onfocus="searchClick()" />
</div>
<div id="search-table" class="superHidden">
<div id="wolfandgrizzly-FAQ-search">
<h1>A to B sales</h1>
<button class="collapsible"><span>What are our shipping policies?</span></button>
<div class="content">
<p>
They are crazy cool.
</p>
</div>
<button class="collapsible"><span>Are you making more products?</span></button>
<div class="content">
<p>
We'll sell you more very soon
</p>
</div>
</div>
I don't see where you are getting that the active class is not being removed. It doesn't appear that there is a problem with that. I think the problem is that you are showing the answers via maxHeight setting, but you aren't resetting it when you click the search box. I've updated it in this snippet, but essentially it boiled down to adding the following in your loop:
var content = faqButtons[i].nextElementSibling
content.style.maxHeight = null
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.maxHeight) {
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
});
}
function searchClick() {
var searchTable;
searchTable = document.getElementById("search-table");
faqButtons = searchTable.getElementsByTagName("button");
for (i = 0; i < faqButtons.length; i++) {
if (faqButtons[i].classList.contains("active")) {
console.log('THIS ONE: ', faqButtons[i]);
faqButtons[i].classList.remove("active");
}
var content = faqButtons[i].nextElementSibling
content.style.maxHeight = null
}
}
.collapsible {
display: flex;
justify-content: space-between;
background-color: white;
color: black;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 20px;
}
.collapsible span {
padding-left: 5px;
padding-right: 5px;
}
.collapsible .active,
.collapsible:hover {
background-color: #333;
color: white;
}
.content {
padding: 0 18px;
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
}
.content p {
margin-top: 10px;
}
<div class="search-faq">
<input autocomplete="off" type="text" id="search-faq" placeholder="Search for FAQ" onfocus="searchClick()" />
</div>
<div id="search-table" class="superHidden">
<div id="wolfandgrizzly-FAQ-search">
<h1>A to B sales</h1>
<button class="collapsible"><span>What are our shipping policies?</span></button>
<div class="content">
<p>
They are crazy cool.
</p>
</div>
<button class="collapsible"><span>Are you making more products?</span></button>
<div class="content">
<p>
We'll sell you more very soon
</p>
</div>
</div>
Here's an approach that uses as little JavaScript as possible. If JavaScript is disabled, the only thing that will break is the search box. Sections will still open and close as expected when clicked.
The accordion stores its state in checkboxes, one in each section. Each section's title is a label element which toggles that section's checkbox when clicked. The sections are expanded and collapsed using CSS :checked selectors.
var sections = [].slice.call(document.querySelectorAll(".accordion li")),
searchAccordion = function() {
var value = document.getElementById("search").value.toLowerCase();
sections.map(function(section) {
var content = section.textContent.toLowerCase();
section.querySelector("input").checked = content.includes(value);
});
};
body {
font-family: sans-serif;
}
.accordion {
padding-left: 0;
margin: -1rem;
}
.accordion li {
list-style-type: none;
}
.accordion input {
display: none;
}
.accordion label {
background-color: #eee;
transition: background-color 100ms;
cursor: pointer;
font-size: 1.2rem;
padding: 1rem;
display: block;
}
.accordion label:hover {
background-color: #444;
color: white;
}
.accordion .content {
padding: 1rem;
display: none;
}
.accordion input:checked ~ .content {
display: block;
}
<input id="search" onKeyup="searchAccordion()" type="text" placeholder="Search for FAQ" autocomplete="off">
<h1>A to B sales</h1>
<ul class="accordion">
<li>
<input type="checkbox" id="section1">
<label for="section1">What are our shipping policies?</label>
<div class="content">They are crazy cool.</div>
</li>
<li>
<input type="checkbox" id="section2">
<label for="section2">Are you making more products?</label>
<div class="content">We'll sell you more very soon</div>
</li>
</ul>

Using jquery and css how do I create a delayed background color change animation?

I have a javascript-powered searchable html list. When the user types in some text that hones the list down to one result, I want the ".opaqueblock" div background to turn into a brighter color and then turn back to the original color after a quick delay. At the moment the color changes smoothly to a brighter when the user types in "Text 53" in the search box, but I can't figure out how to change the color back to the original darker color. I want a bright flash effect. Also, the each() function that I try to use to only change the background color of the isolated item "Text 53" is changing the background color of all the items. I do not want the color to change for list items that are hidden. Does anyone know a solution?
<script type="text/javascript" src="https://code.jquery.com/jquery-1.9.0.min.js""></script>
<script type="text/javascript" src="https://code.jquery.com/color/jquery.color-2.1.2.min.js"></script>
<link href='https://fonts.googleapis.com/css?family=Comfortaa' rel='stylesheet'> <!--for search text-->
<link href='https://fonts.googleapis.com/css?family=Nunito' rel='stylesheet'> <!--for search text-->
<script>
function ListSearch() {
var input, filter, ul, li, a, i, txtValue, resultsCount = 0, resultText = " results"; // Declare variables
input = document.getElementById('myInput');
filter = input.value.toUpperCase();
ul = document.getElementById("DocumentList");
li = ul.getElementsByTagName('li');
$('.opaqueBlock').css('border-color','transparent') //remove all borders in case a border was added when the results were honed down to 1
for (i = 0; i < li.length; i++) { // Loop through all list items, hide those that don't match query
a = li[i].getElementsByTagName("div")[0];
txtValue = a.textContent || a.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
resultsCount++;
} else {
li[i].style.display = "none";
}
}
if (resultsCount == 1) {
$('.opaqueBlock').css('border-color','#68e8d5');
$('.PreviewArrow').css("opacity", "1"); document.getElementById("results_span").style.color = "#b6fbd2";
resultText = " result";
$(".opaqueBlock:visible").each(
$('.opaqueBlock').delay(300).css("background-color", "rgba(147,185,212,0.6)")
/* $('.opaqueBlock').delay(300).css("background-color", "rgba(72,97,115,0.6)"); */
);
}
else if(resultsCount < 1){document.getElementById("results_span").style.color = "#fbb6bc";}
else if(resultsCount < 3){document.getElementById("results_span").style.color = "#b6fbd2";}
else if(resultsCount < 4){document.getElementById("results_span").style.color = "#b6d7fb";}
else if(resultsCount < 5){document.getElementById("results_span").style.color = "#eeb8ff";}
else {document.getElementById("results_span").style.color = "#fbb6bc";}
document.getElementById("results_span").innerHTML = resultsCount + resultText;
}
$(document).ready(function(){
function HoverOverItem(thisParticularItem, DocImageLink){
if (!thisParticularItem.hasClass("active_form_item") ) {
$(".PreviewContainer").css("opacity", "0.3");
$(".PreviewContainer").css("background-image", DocImageLink);
$(".PreviewContainer").animate({'opacity': '1'},500);
$("#ContentContainer").find('.active_form_item').removeClass('active_form_item');
thisParticularItem.addClass("active_form_item");
}
}
});
$(window).load(function(){
$("#myInput").focus();
var ul, li, resultsCount = 0, resultText = " searchable documents"; // Declare variables
ul = document.getElementById("DocumentList");
li = ul.getElementsByTagName('li');
for (i = 0; i < li.length; i++) {resultsCount++;} // Loop through all list items to get a count
document.getElementById("results_span").innerHTML = resultsCount + resultText;
});
</script>
<style>
#ContentContainer{
height: 700px;
background-color: #132e54;
}
#ContentContainer a{text-decoration: none;}
#ContentContainer img{border: none;}
#search_prompt_div{margin-left: 16px;}
#search_prompt_div p{color: white; font-size: 14px; margin-bottom: 12px; font-family: comfortaa;}
#search_prompt_div input{height: 25px;font-size: 16px; width: 300px; font-family: comfortaa;}
#search_prompt_div #results_span{display: inline-block;margin-left: 6px; color: #fbb6bc; font-size: 12px;}
#DocumentListContainer{
height: 600px;
width: 660px;
overflow: hidden;
}
#DocumentList{
list-style-type: none;
margin: 0;
padding: 6px 0 0 0;
width: 105%;
overflow: auto;
height: 893px;
}
.opaqueBlock{
opacity: 1; /*can set this to 0 if you want to animate it*/
margin-left: 56px; /*can set this to 0 if you want to animate it*/
display: inline-block;
width: 490px;
height: 35px;
border: 2px solid transparent;
background-color: rgba(72,97,115,0.6);
border-radius: 10px;
-webkit-transition: all .6s ease;
-moz-transition: all .6s ease;
-ms-transition: all .6s ease;
-o-transition: all .6s ease;
}
.opaqueBlock:hover{
border: 2px solid #5cb1d8;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=70)";
opacity:0.7 !important;
}
.fileLinkContainer{
width: 630px;
height: 37px;
margin-bottom: 10px;
position: relative;
}
.fileTextSpan{
margin-left: 10px;
color: white;
margin-top: 5px;
font-family: 'Nunito' !important;
font-size: 19px !important;
display: inline-block;
}
</style>
<div id="ContentContainer">
<br><br><br>
<div id="search_prompt_div">
<p>Type something in the search box to find what you are looking for:</p>
<input id="myInput" type="text" placeholder="Search..." onkeyup="ListSearch()">
<span id="results_span"></span>
</div>
<br/><br/><br/>
<div class="PreviewContainer"></div>
<div id="DocumentListContainer">
<ul id="DocumentList">
<li><div class="fileLinkContainer"><div class="opaqueBlock"><span class="fileTextSpan">Text 1</span></div></div></li>
<li><div class="fileLinkContainer"><div class="opaqueBlock"><span class="fileTextSpan">Text 11</span></div></div></li>
<li><div class="fileLinkContainer"><div class="opaqueBlock"><span class="fileTextSpan">Text 21</span></div></div></li>
<li><div class="fileLinkContainer"><div class="opaqueBlock"><span class="fileTextSpan">Text 33</span></div></div></li>
<li><div class="fileLinkContainer"><div class="opaqueBlock"><span class="fileTextSpan">Text 53</span></div></div></li>
</ul>
</div>
</div>
What about to use CSS keyframes somehow like this?
#keyframes anim {
0% { background-color: #fff; }
50%, 70% { background-color: #333; }
100% { background-color: #fff; }
}
Then just toggle class on the element by JS

how to change gently div's size by using animation or something, when it's clicked

I'm just trying to change gently div's size.
When I click that div, I can unfold and fold those h5 elements
but how can I adapt animation about this action?
I already tried to apply keyframe animation or css transition, but I failed
for (var i = 1; i <= 5; i++) {
$('#sampleNumberList').append('<h5>#' + i + '</h5>');
}
$('#topSampleNum').on('click', function() {
if ($('#sampleNumberList').hasClass('active') === true) {
$('#sampleNumberList').removeClass('active');
} else {
$('#sampleNumberList').addClass('active');
}
});
.topFloatBar {
position: fixed;
width: 100%;
top: 0;
z-index: 5;
text-align: center;
background-color: #e3e3e3;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
#sampleNumberList {
max-height: 0;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="topFloatBar" id="topSampleNum">
<h2 id="mainNumber"> #1 </h2>
<div id="sampleNumberList"> </div>
</div>
Like this?
var sampleList = document.querySelector('#sampleNumberList');
var listTrigger = document.querySelector('#mainNumber');
for(var i=1; i<=10; i++){
let el = document.createElement('h5');
el.innerText = i;
sampleList.append(el);
}
listTrigger.addEventListener('click', ()=> {sampleList.classList.toggle('active');});
.topFloatBar{
position:fixed;
width:100%;
top: 0;
z-index:5;
text-align:center;
background-color: #e3e3e3;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
cursor:pointer;
}
#sampleNumberList {
position: absolute;
width: 100%;
overflow:hidden;
height: auto;
transform: scale(0);
transition: transform 0.5s ease-in-out;
transform-origin: top;
}
.active{transform: scale(1) !important;}
<div class="topFloatBar" id="topSampleNum" >
<h2 id="mainNumber">#1</h2>
<div id="sampleNumberList"></div>
</div>
Use jQuery slideToggle() to smoothly toggle those h5.
See Demo: JSFiddle
$('#clickableDiv').click(function() {
$('h5').slideToggle();
});
#sampleNumberList>h5 {
display: none
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div style='background:red'>
<h2 id='mainNumber'> #1 </h2>
<div id='clickableDiv' style='background:yellow'> Click Me </div>
<div id='sampleNumberList' style='background:blue'>
<h5> #1 </h5>
<h5> #2 </h5>
<h5> #3 </h5>
</div>
</div>

jquery on handler not working for inserted element

I've got a simple to-do list app. To-do items are inserted by jQuery as <li> items. When they're checked off, they're removed from #todolist and prepended to #donelist. I want to let the user replace to-do items they've accidentally checked off, hence the .on handler for #donelist .checkbox elements, but it's not working. I've been puzzling over this for an embarrassingly long amount of time. How can I get the click handler working for #donelist .checkboxes?
HTML:
<div id="topform">
<input type="text" id="task" placeholder=" New task...">
</div>
<ul id="todolist">
</ul>
<ul id="donelist">
</ul>
JS:
$('#todolist').on('click', '.checkbox', checkTask);
$('#donelist').on('click', '.checkbox', replaceTask);
$('input').keypress(function (e) {
if (e.which == 13) {
addTask(e);
}
});
function addTask(e) {
taskToAdd = $('#task').val();
var listItem = "<li><span class='todotask'>" + taskToAdd + "</span><div class='checkbox'></div></li>";
$('#todolist').prepend(listItem);
}
function checkTask() {
var listItem = $(this).parent();
listItem.remove();
$('#donelist').prepend(listItem);
}
function replaceTask() {
alert('hey buddy');
}
Full CSS:
html,
body {
margin: 0;
padding: 0;
background-color: #313131;
font-family: 'Helvetica', sans-serif;
}
#task {
width: 98%;
margin: 5px auto 7px auto;
padding: 0;
display: block;
height: 45px;
border: none;
border-radius: 2px;
font-size: 25px;
background-color: #F7F7F7;
}
ul {
margin: 0 auto 0 auto;
padding: 0;
width: 98%;
}
li {
list-style-type: none;
padding: 0;
margin: 5px auto 0 auto;
width: 100%;
height: 45px;
line-height: 45px;
position: relative;
font-size: 25px;
border-radius: 2px;
background-color: #F7F7F7;
}
#donelist li {
opacity: .5;
text-decoration: line-through;
}
.todotask {
margin-left: 7px;
}
.checkbox {
height: 31px;
width: 31px;
border-radius: 2px;
background-color: #C1C1C1;
position: absolute;
right: 7px;
top: 7px;
}
checkTask() works just fine, which is what really confuses me. checkTask() is called when the user clicks on a dynamically inserted element (a div in a li that's inserted by addTask(). Why doesn't replaceTask() fire as well?
Having the corresponding HTML in the OP would have helped, so I've had to guess a bit about how the structure, but here's a working example of what I think you're looking for:
HTML
<h1>ADD</h1>
<input id="task"></input>
<button id="add">Add</button>
<h1>TODO</h1>
<ul id="todolist">
<li><span class='todotask'>" Take out the garbage "</span><div class='checkbox'></div></li>
<li><span class='todotask'>" Do the dishes "</span><div class='checkbox'></div></li>
</ul>
<h1>DONE</h1>
<ul id="donelist">
</ul>
CSS
.checkbox{
width: 15px;
height: 15px;
background-color: black;
display: inline-block;
cursor: pointer;
}
JavaScript inside document.ready()
$('#todolist').on('click', '.checkbox', checkTask);
$('#donelist').on('click', '.checkbox', replaceTask);
$("#add").click(addTask);
function addTask(e) {
taskToAdd = $('#task').val();
var listItem = "<li><span class='todotask'>" + taskToAdd + "</span><div class='checkbox'></div></li>";
$('#todolist').prepend(listItem);
}
function checkTask() {
var listItem = $(this).parent();
listItem.remove();
$('#donelist').prepend(listItem);
}
function replaceTask() {
var listItem = $(this).parent();
listItem.remove();
$('#todolist').prepend(listItem)
}

Categories

Resources