ul - li to 2 drop targets - javascript

I am trying to put together this project.
In my list I have fruit & veg.I want to be able to drag the right item into the correct box. Once it is in the correct box ( dropped) it should be invisible.
Hope someone can help.
HTML
<header>
<h1>THIS IS A TEST PAGE</h1>
</header>
<nav>
</nav>
<section>
<h1>Choose a Box</h1>
<ul id="fruit">Fruit
</ul>
<ul id="veg">Veg
</ul>
</section>
<article>
<ul id="dragsource">
<li id="item1" draggable="true">Apple</li>
<li id="item2" draggable="true">Banana</li>
<li id="item3" draggable="true">Orange</li>
<li id="item4" draggable="true">Potato</li>
<li id="item5" draggable="true">Carrot</li>
<li id="item6" draggable="true">Pea</li>
</ul>
</article>
JS
window.onload = function() {
var target1 = document.getElementById("fruit");
var target2 = document.getElementById("veg");
var list = document.querySelectorAll("#dragsource li");
for (i = 0; i < list.length; i++) {
list[i].draggable = true;
list[i].ondragstart = function(event) {
var event = event || window.event;
var dt = event.dataTransfer;
dt.setData("text", event.target.id);
dt.effectAllowed = "move";
};
}
target1.ondragover = function(event) {
var event = event || window.event;
event.preventDefault();
};
target2.ondragover = function(event) {
var event = event || window.event;
event.preventDefault();
};
target2.ondrop = function(event) {
var event = event || window.event;
var dt = event.dataTransfer;
event.preventDefault();
var data = dt.getData("text");
target2.appendChild(document.getElementById("data"));
};
target1.ondrop = function(event) {
var event = event || window.event;
var dt = event.dataTransfer;
event.preventDefault();
var data = dt.getData("text");
target1.appendChild(document.getElementById(data));
};
};
CSS
header {
background-color: black;
color: yellow;
text-align: center;
padding: 5px;
}
nav {
line-height: 30px;
background-color: yellow;
height: 400px;
width: 100px;
float: left;
padding: 5px;
}
body,
html {
background-color: silver;
font-family: sans-serif;
color: black;
text-align: center;
}
section {
width: 482px;
height: 220px;
float: left;
text-align: center;
padding: 5px;
}
#fruit {
width: 90px;
height: 120px;
left: 150px;
top: 150px;
padding: 10px;
border: 2px solid green;
position: absolute;
}
#veg {
width: 200px;
height: 120px;
left: 340px;
top: 150px;
padding: 10px;
border: 2px solid green;
position: absolute;
}
article {
background-color: aqua;
height: 170px;
width: 482px;
float: right;
padding: 5px;
}
ul {
margin: left;
column-count: 3;
width: 50%;
text-align: left;
list-style: none;
}
li {
list-style-type: none;
padding: 5px;
margin: 2px;
background-color: #CCCCFF;
border: 2px double #CCCCCC;
}
footer {
background-color: black;
color: white;
clear: both;
text-align: center;
padding: 5px;
}
See fiddle: https://jsfiddle.net/cq2aw1dy/3/

Before starting - it should be noted in your target2.onDrop function, the last line you say document.getElementById("data") there shouldn't be any quotes there. That will give you some issues.
Since all of your fruits have ID's I think it would serve you better to make use of classes The way to do that would be to add a class to the dropped element that tells it to become invisible.
CSS
#fruit > .hiddenDrop, #veg > .hiddenDrop {
display: none;
}
Javascript - Place this inside of your .onDrop() functions
var element = document.getElementById(data);
element.setAttribute('class', 'hiddenDrop');
target.appendChild(element);
What this does is after the element is dropped, it adds the class hiddenDrop to that element which will then change it's display property to none

EDIT
You can do it with the css like this #fruit > *{display: none;}
It is saying if you drag into the fruit div, display is set to none.
See fiddle:
https://jsfiddle.net/cq2aw1dy/4/

Related

When to Call JavaScript Toggle Function?

I have a drop down menu I need to make appear and disappear using pure JavaScript (no libraries/jQuery). Thus I am developing a toggle function. However despite trying several approaches, nothing seems to work. My current idea is to create a variable to hold the state of the menu (open or closed). Once the display of the menu changes from "none" to "block", the variable should change from "closed" to "open". Then an event listener would be added to the body element so when anything is clicked, the menu closes (i.e. the display property is changed back to "none").
Unfortunately the above doesn't seem work. When I put the If/else block outside of an event listener it fires when the page loads, but not when the menuToggle variable changes. If I put it or a function inside the menuPlaceholder event listener the menu won't open, probably due to the open and close code being called basically at the same time.
Clearly I am missing something, probably related to program control or function calling. Does anyone have any insights?
The code I am working with is below. Note the alert functions peppered throughout the code are for testing purposes only.
//Puts IDs for search preference selection box into variables
var menuPlaceholder = document.getElementById('searchSelection');
var menuDisplay = document.getElementById('searchOptions');
var boxLabel = document.getElementById('searchLabel');
//Puts IDs for text input box and submission into variables
var searchBoxPlaceholder = document.getElementById('searchInput');
var searchInput = document.getElementById('searchBox');
var submitButton = document.getElementById('submit');
//Adds class to each search option and puts ID of hidde field into variable
var searchPrefSubmission = document.getElementsByClassName('buttonSearch');
var hiddenInput = document.getElementById('searchChoice');
//Global variable to indicate whether searchOptions menu is opened or closed
var menuToggle = "closed";
//Closes element when one clicks outside of it.
function hideOnClickOutside(element) {
const outsideClickListener = event => {
if (!element.contains(event.target) && isVisible(element)) { // or use: event.target.closest(selector) === null
element.style.display = 'none'
removeClickListener()
}
}
const removeClickListener = () => {
document.removeEventListener('click', outsideClickListener)
}
document.addEventListener('click', outsideClickListener)
}
const isVisible = elem => !!elem && !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length )
//When the placeholder box is clicked, the option menu appears
menuPlaceholder.addEventListener('click', function (event){
menuDisplay.style.display = "block";
menuToggle = "open";
//Add click event to searchPref buttons
for (i = 0; i < searchPrefSubmission.length; i++) {
//Assigns value of the button to both the hidden input field and the placeholder box
searchPrefSubmission[i].addEventListener('click', function(event) {
hiddenInput.value=this.value;
boxLabel.innerHTML = this.value;
menuDisplay.style.display = "none";
menuPlaceholder.style.display = "inline-block";
});
}
});
//This code causes the text input box of the search form to appear when the background box is clicked
searchBoxPlaceholder.addEventListener('click', function(event){
searchInput.style.display = "inline";
submitButton.style.display = "inline";
//hideOnClickOutside(menuDisplay);
});
if (menuToggle == "open"){
document.body.addEventListener('click', function(event){
alert('Foo!');
})
}else{
alert('Boo!');
}
/*function toggleMenu () {
//menuDisplay.style.display = "none";
alert('Boo!');
menuToggle = "closed";
}*/
body {
font-family:Montserrat, sans-serif;
}
#searchOptionPlaceholder {
display: inline-block;
}
#searchSelection {
padding: 10px 20px;
margin-right: 10px;
background-color: #F0F3F5;
display: inline-block;
color: #000000;
width: 140px;
max-width: 200px;
max-height: 35px;
border: 2px solid black;
vertical-align: middle;
}
#searchSelection img {
float: right;
}
#searchLabel {
display: inline-block;
padding-top: 10px;
vertical-align: top;
}
#searchOptions {
display: none;
background-color: #F0F3F5;
position: absolute;
z-index: 2;
}
#searchOptions ul {
background-color: #F0F3F5;
padding: 5px;
}
#searchOptions li {
list-style-type: none;
border-bottom: 2px solid black;
}
#searchOptions li:hover {
background-color: #706868;
color: #ffffff;
}
.buttonSearch {
background-color: transparent;
border: none;
padding: 10px;
font-size: 14px;
}
.searchSubHeading {
font-size: 12px;
}
#searchInput {
display: inline-block;
background-color: #F0F3F5;
padding: 10px 100px;
position: relative;
top: 0px;
max-width: 350px;
border: 2px solid black;
vertical-align: middle;
}
#searchInput img {
position: relative;
left: 80px;
}
#searchBox {
display: none;
width: 80%;
background-color: #F0F3F5;
border: none;
font-size: 1.5em;
position: relative;
right: 50px;
vertical-align: middle;
}
#submit {
border: none;
background-image: url('https://library.domains.skidmore.edu/search/magnifyingGlass.png');
background-repeat: no-repeat;
background-size: contain;
width: 50px;
height: 30px;
position: relative;
right: -80px;
vertical-align: middle;
}
#otherLinks {
margin-top: 10px;
}
#otherLinks a{
color: #000000;
}
#otherLinks a:hover{
color: #006a52;
}
<h1>Library Search</h1>
<form method="post" action="https://library.domains.skidmore.edu/search/searchBox.php" id="librarySearch">
<div id="searchSelection"><span id="searchLabel">Catalog</span><img src="down.png" height="30px" width="30px" /></div>
<div id="searchOptions">
<ul>
<li><button type="button" name="searchPref" value="Catalog" class="buttonSearch">Catalog<br /><br /><span class="searchSubHeading">Search books and DVDs</span></button></li>
<li><button type="button" name="searchPref" value="SearchMore" class="buttonSearch">SearchMore<br /><br /><span class="searchSubHeading">Search everything</span></button></li>
<li><button type="button" name="searchPref" value="Journals" class="buttonSearch">Journals<br /><br /><span class="searchSubHeading">Search journals</span></button></li>
</ul>
</div>
<div id="searchInput">
<input type="hidden" id="searchChoice" name="searchPref" value="catalog" />
<input type="search" id="searchBox" size="60" name="searchText" placeholder="Search our holdings"/><button type="submit" id="submit"></button></div>
<div id="otherLinks">Advanced Catalog Search | WorldCat | eBooks</div>
</form>
Some issues:
Adding event listeners within an event listener is in most cases a code smell: this will add those inner listeners each time the outer event is triggered. Those listeners remain attached, and so they accumulate. So, attach all event handlers in the top-level script, i.e. on page load, and then never again.
The if ... else at the end will execute on page load, and then never again. So the value of menuToggle is guaranteed to be "closed". You need to put that if...else switch inside the handler, so that it executes every time the event triggers, at which time the menuToggle variable will possibly have a modified value.
The body element does not stretch (by default) over the whole window. If you want to detect a click anywhere on the page, you should attach the listener on the document element itself, not on document.body.
When the click on the menu placeholder is handled, you should avoid that this event "bubbles" up the DOM tree up to the document, because there you have the other handler that wants to hide the menu again. You can do this with event.stopPropagation().
The global variable is not absolutely necessary, but if you use it, then I would call it menuVisible and give it a boolean value: false at first, and possibly true later.
For actually toggling the menu, I would create a function, which takes the desired visibility (false or true) as argument, and then performs the toggle.
Do not use undeclared variables, like the for loop variable i. Define it with let.
Here is your code with those changes implemented. Of course, there is still a lot that could be improved, but I believe that goes beyond the scope of this question:
var menuPlaceholder = document.getElementById('searchSelection');
var menuDisplay = document.getElementById('searchOptions');
var boxLabel = document.getElementById('searchLabel');
var searchBoxPlaceholder = document.getElementById('searchInput');
var searchInput = document.getElementById('searchBox');
var submitButton = document.getElementById('submit');
var searchPrefSubmission = document.getElementsByClassName('buttonSearch');
var hiddenInput = document.getElementById('searchChoice');
// Changed name and type of global variable:
var menuVisible = false;
// Removed some functions ...
menuPlaceholder.addEventListener('click', function (event){
// Use new function for actually setting the visibility
toggleMenu(!menuVisible);
// Avoid that click event bubbles up to the document level
event.stopPropagation();
});
// Add these event handlers on page load, not within another handler
// Define loop variable with let
for (let i = 0; i < searchPrefSubmission.length; i++) {
//Assigns value of the button to both the hidden input field and the placeholder box
searchPrefSubmission[i].addEventListener('click', function(event) {
hiddenInput.value = this.value;
boxLabel.innerHTML = this.value;
// Use the new function for setting the visibility
toggleMenu(false);
menuPlaceholder.style.display = "inline-block";
});
}
searchBoxPlaceholder.addEventListener('click', function(event){
searchInput.style.display = "inline";
submitButton.style.display = "inline";
});
// Bind handler on document itself, and call new function
document.addEventListener('click', function(event) {
toggleMenu(false);
});
// new function to perform the toggle
function toggleMenu(show) {
menuDisplay.style.display = show ? "block" : "none";
menuVisible = show;
}
body {
font-family:Montserrat, sans-serif;
}
#searchOptionPlaceholder {
display: inline-block;
}
#searchSelection {
padding: 10px 20px;
margin-right: 10px;
background-color: #F0F3F5;
display: inline-block;
color: #000000;
width: 140px;
max-width: 200px;
max-height: 35px;
border: 2px solid black;
vertical-align: middle;
}
#searchSelection img {
float: right;
}
#searchLabel {
display: inline-block;
padding-top: 10px;
vertical-align: top;
}
#searchOptions {
display: none;
background-color: #F0F3F5;
position: absolute;
z-index: 2;
}
#searchOptions ul {
background-color: #F0F3F5;
padding: 5px;
}
#searchOptions li {
list-style-type: none;
border-bottom: 2px solid black;
}
#searchOptions li:hover {
background-color: #706868;
color: #ffffff;
}
.buttonSearch {
background-color: transparent;
border: none;
padding: 10px;
font-size: 14px;
}
.searchSubHeading {
font-size: 12px;
}
#searchInput {
display: inline-block;
background-color: #F0F3F5;
padding: 10px 100px;
position: relative;
top: 0px;
max-width: 350px;
border: 2px solid black;
vertical-align: middle;
}
#searchInput img {
position: relative;
left: 80px;
}
#searchBox {
display: none;
width: 80%;
background-color: #F0F3F5;
border: none;
font-size: 1.5em;
position: relative;
right: 50px;
vertical-align: middle;
}
#submit {
border: none;
background-image: url('https://library.domains.skidmore.edu/search/magnifyingGlass.png');
background-repeat: no-repeat;
background-size: contain;
width: 50px;
height: 30px;
position: relative;
right: -80px;
vertical-align: middle;
}
#otherLinks {
margin-top: 10px;
}
#otherLinks a{
color: #000000;
}
#otherLinks a:hover{
color: #006a52;
}
<h1>Library Search</h1>
<form method="post" action="https://library.domains.skidmore.edu/search/searchBox.php" id="librarySearch">
<div id="searchSelection">
<span id="searchLabel">Catalog</span>
<img src="down.png" height="30px" width="30px" />
</div>
<div id="searchOptions">
<ul>
<li>
<button type="button" name="searchPref" value="Catalog" class="buttonSearch">
Catalog<br /><br /><span class="searchSubHeading">Search books and DVDs</span>
</button>
</li>
<li>
<button type="button" name="searchPref" value="SearchMore" class="buttonSearch">
SearchMore<br /><br /><span class="searchSubHeading">Search everything</span>
</button>
</li>
<li>
<button type="button" name="searchPref" value="Journals" class="buttonSearch">
Journals<br /><br /><span class="searchSubHeading">Search journals</span>
</button>
</li>
</ul>
</div>
<div id="searchInput">
<input type="hidden" id="searchChoice" name="searchPref" value="catalog" />
<input type="search" id="searchBox" size="60" name="searchText" placeholder="Search our holdings"/>
<button type="submit" id="submit"></button>
</div>
<div id="otherLinks">
Advanced Catalog Search |
WorldCat |
eBooks
</div>
</form>

How to display the value of a "number type input" in a span next to it

I have a number input type element. I want its value to be displayed in the span next to it. As I click or scroll on the increase or decrease button inside the input. I want the span's relative value to change. Is it possible??
I have tried to get the value and display in span but did not worked.
Javascript
var displayer = document.getElementById('displayer');
var day = document.getElementById('days');
var hrs = document.getElementById('hours');
var s_t = document.getElementById('s_t');
function funcday() {
day.style.border = "1.5px solid darkorange";
hrs.style.border = "1.5px solid mediumaquamarine";
s_t.style.border = "1.5px solid darkorange";
s_t.setAttribute("max", 365);
var d = s_t.value;
document.getElementById('display_d').textContent = d;
}
function funhrs() {
day.style.border = "1.5px solid mediumaquamarine";
hrs.style.border = "1.5px solid darkorange";
s_t.style.border = "1.5px solid darkorange";
s_t.setAttribute("max", 48);
var h = s_t.value;
document.getElementById('display_h').textContent = h;
}
#container {
display: flex;
}
.st {
width: 60px;
margin-right: 10px;
}
#days,
#hours {
color: #fff;
width: 70px;
margin-left: 10px;
padding: 6px;
cursor: pointer;
background-color: mediumaquamarine;
border: 1.5px solid mediumaquamarine;
text-align: center;
font-family: 'Courier';
}
.displayer {
align-content: center;
text-align: center;
line-height: 2;
margin-left: 20px;
margin-right: 20px;
font-size: 15px;
border: 1px solid white;
display: block;
float: right;
}
.displayer span {
margin-right: 5px;
margin-left: 5px;
}
<div id="container">
<input type="number" min="1" class="st" id="s_t" autocomplete="off">
<div id="days" onclick="funcday();">Days</div>
<div id="hours" onclick="funhrs();">Hours</div>
<div id="displayer" class="displayer">
<span id="display_d">00</span><span>Days</span>
<span id="display_h">00</span><span>Hours</span>
</div>
</div>
You should add onchange event handler to the input element and change text of the span element inside this handler.

Javascript Dropdown Staying open

In my site I made a simple dropdown menu, but my problem is that it won't close if mouseleave happens on the <span> that triggers the dropdown.
Here is my code:
//Find the dropdown span
var header = document.getElementById('drop');
//Find the ul with the links
var ul = document.getElementById('nav-dropdown');
//Get the width and apply it to the dropdown items
var width = drop.getBoundingClientRect().width;
ul.style.minWidth = width + "px";
//Round the corners on the last link
var links = document.getElementsByClassName('dropdown-link');
links[links.length - 1].style.borderRadius = "0 0 7px 7px";
var open = 0;
//Onhover, display the dropdown;
header.addEventListener("mouseover", function() {
ul.style.display = "block";
header.style.borderRadius = "7px 7px 0 0";
if (links[0].getBoundingClientRect().width > width) {
links[0].style.borderRadius = "0 7px 0 0";
}
open = 1;
});
//When the mouse leaves the menu, close it.
ul.addEventListener("mouseleave", function() {
ul.style.display = "none";
header.style.borderRadius = "7px";
open = 0;
});
//What I've tried to fix it:
/*
header.addEventListener("mouseleave", function() {
ul.style.display = "none";
header.style.borderRadius = "7px";
});
*/
/*Stylesheet for this stuff*/
* {
font-family: arial;
margin: 0;
padding: 0;
box-sizing: border-box;
font-size: 20px;
text-decoration: none;
list-style: none;
}
a:visited {
color: white;
}
a,
#drop {
color: white;
}
a:hover {
color: coral;
}
.header-links-container {
position: relative;
top: 0;
background: rgb(63, 83, 95);
width: 100%;
height: 80px;
opacity: .8;
z-index: 999;
}
.title {
font-weight: bold;
font-size: 30px;
padding: 20px 50px;
position: relative;
float: left;
color: white;
}
.header-links {
position: relative;
float: right;
vertical-align: middle;
}
.nav-links {
margin: auto;
padding-top: 20px;
padding-right: 30px;
}
.nav-link {
position: relative;
float: right;
padding: 0 20px;
font-size: 23px;
padding: 5px 10px;
margin: 5px;
background: #4471ba;
border-radius: 7px;
}
.nav-link:hover {
background: #4480ba;
color: #d1d1d1;
}
#nav-dropdown {
display: none;
margin-top: 42px;
margin-left: 5px;
position: absolute;
}
.dropdown-link {
color: black;
background-color: #ccc;
padding: 5px 10px;
}
.dropdown-link:hover {
color: #000;
background-color: #a7a7a7;
}
.dropdown-link:active {
color: white;
background-color: #3b8cfa;
}
<div class="header-links-container">
<h2 class="title">Title</h2>
<div class="header-links">
<ul class="nav-links">
<li class="nav-link">Photo Gallery</li>
<li class="nav-link">SLAP</li>
<li id="drop" class="nav-link"><span>Dropdown</span></li>
<ul id="nav-dropdown" class="jim">
<a href="#">
<li class="dropdown-link">Link 1</li>
</a>
<a href="#">
<li class="dropdown-link">Link 2</li>
</a>
<a href="#">
<li class="dropdown-link">Longer Link</li>
</a>
<a href="#">
<li class="dropdown-link">Vacuum</li>
</a>
</ul>
</ul>
</div>
</div>
<p>
Relavent JS lines start at Line 16
</p>
And here is the fiddle that might make more sense: https://jsfiddle.net/dLw1hu5n/6/
I've tried closing the dropdown like in the last code block, but then it won't stay open when you go to hover over the links. I've also tried making the menu close when the mouse hovers over the navbar div, but no luck there either.
Can I fix this or do I need to start from square 1?
I would prefere to solve this via css. However, in your case you can try the following:
function displayDropdown() {
ul.style.display = "block";
header.style.borderRadius = "7px 7px 0 0";
if (links[0].getBoundingClientRect().width > width) {
links[0].style.borderRadius = "0 7px 0 0";
}
open = 1;
}
function hideDropdown() {
ul.style.display = "none";
header.style.borderRadius = "7px";
open = 0;
}
//Onhover, display the dropdown;
header.addEventListener("mouseover", function() {
displayDropdown();
});
ul.addEventListener("mouseover", function() {
displayDropdown();
});
//When the mouse leaves the menu, close it.
ul.addEventListener("mouseleave", function() {
hideDropdown();
});
header.addEventListener("mouseleave", function() {
hideDropdown();
});
Your JS is fine but your event listener for mouseleave needs to be on the enclosing div. This way your element stays open until you hover outside of the header
t.addEventListener("mouseleave", function() {
ul.style.display = "none";
header.style.borderRadius = "7px";
open = 0;
});
What is t?
var t = document.getElementById(t);
What element has id T?
Try this fiddle to find out https://jsfiddle.net/dLw1hu5n/12/

Continue propagation

I have some nested elements on my page with a same handler on them which should be called only for an event target without affecting elements higher in DOM tree. To achieve this behavior I used stopPropagation method and it was ok. Then I had to add some handlers for body and other elements outside the nested divs which should be called in any case. Of course stopPropagation isn't an option now but how can I make it work?
Here is a sample:
html:
<div id="container">
<div id="nested1" class="nested">
<div id="nested2" class="nested">
<div id="nested3" class="nested">
<div id="no-handler"></div>
</div>
</div>
</div>
</div>
css:
#container {
display: block;
width: 398px;
height: 398px;
padding: 30px;
border: solid 1px #888;
}
#nested1 {
width: 336px;
height: 336px;
padding: 30px;
}
#nested2 {
width: 274px;
height: 274px;
padding: 30px;
}
#nested3 {
width: 212px;
height: 212px;
padding: 30px;
}
#no-handler {
width: 150px;
height: 150px;
padding: 30px;
border: solid 1px #888;
}
.nested {
border: solid 1px #888;
}
.nested-clicked {
background-color: red;
}
.outer-clicked {
background-color: green;
}
js:
var container = document.getElementById("container");
var nested = document.getElementsByClassName("nested");
function outerHandler(e) {
this.classList.add("outer-clicked");
}
function nestedHandler(e) {
e.stopPropagation();
this.classList.add("nested-clicked");
}
container.addEventListener("click", outerHandler, false);
document.body.addEventListener("click", outerHandler, false);
for (var i = 0; i < nested.length; i++) {
nested[i].addEventListener("click", nestedHandler, false);
}
jsfiddle link:
http://jsfiddle.net/6kgnu7fr/
clicking on .nested should add red background color to clicked element and add green color to outer body and #container
UPD:
http://jsfiddle.net/6kgnu7fr/2/
clicking on #no-event or any other element inside .nested should also call nestedHandler for this .nested element.
You can check for the event's target in your nestedHandler instead of stopping the propagation. Change the class only if the target is this so that the effet will only be applied for the div on which the event occurred:
function nestedHandler(e) {
if (e.target === this) {
this.classList.add("nested-clicked");
}
}
Edit
Following your edit, this is harder. Way to do it is to find e.target's first ancestor with the "nested" class, then doing the comparison with it instead of target:
function findAncestorWithClass(dom, targetClass){
if(!dom){
return; // (undefined)
}
if(dom.classList.contains(targetClass)){
return dom;
}
// terminal recursion
return findAncestorWithClass(dom.parentNode, targetClass);
}
This is naïve shot. You may want to look for a way to make it more efficient, e.g. by avoiding to look for the first ancestor on each .nested div.
See the working snipped below.
var container = document.getElementById("container");
var nested = document.getElementsByClassName("nested");
function outerHandler(e) {
this.classList.add("outer-clicked");
}
function findAncestorWithClass(dom, targetClass){
if(!dom){
return; // (undefined)
}
if(dom.classList.contains(targetClass)){
return dom;
}
// terminal recursion
return findAncestorWithClass(dom.parentNode, targetClass);
}
function nestedHandler(e) {
var nestedParent = findAncestorWithClass(e.target, "nested");
if (this === nestedParent) {
nestedParent.classList.add("nested-clicked");
}
}
container.addEventListener("click", outerHandler, false);
document.body.addEventListener("click", outerHandler, false);
for (var i = 0; i < nested.length; i++) {
nested[i].addEventListener("click", nestedHandler, false);
}
#container {
display: block;
width: 398px;
height: 398px;
padding: 30px;
border: solid 1px #888;
}
#nested1 {
width: 336px;
height: 336px;
padding: 30px;
}
#nested2 {
width: 274px;
height: 274px;
padding: 30px;
}
#nested3 {
width: 212px;
height: 212px;
padding: 30px;
}
#sub-nested {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
}
.nested {
border: solid 1px #888;
}
.nested-clicked {
background-color: red;
}
.outer-clicked {
background-color: green;
}
<div id="container">
<div id="nested1" class="nested">
<div id="nested2" class="nested">
<div id="nested3" class="nested">
<div id="sub-nested"></div>
</div>
</div>
</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