I am trying to add an event listener on my JavaScript file to link it to my HTML however, it does not recognise it. Am I doing something wrong because they are in separate text documents. However, it works with my other sections so I think it might be a small mistake that I have not spotted out yet.
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<head>
<title>Outliner</title>
<link href="style.css" rel="stylesheet" title="Style">
<script type= "text/javascript" src='setting.js'></script>
<div>
<input type="button" onclick="reset();" value="Reset form">
<button id="pr" onclick="reset()"> Clear </button>
<div id="ul" class="editor" contenteditable="true" draggable="true" ></div>
</div>
</head>
</body>
const TAB_KEY = 9;
const ENTER_KEY = 13;
let editor = document.querySelector('.editor');
editor.appendChild(document.createElement('li')); // Add initial (for visibility)
window.editor.addEventListener('focus', (e) => {
//let ul = e.target;
//let li = document.createElement('li');
//ul.appendChild(li);
});
window.editor.addEventListener('keydown', (e) => {
let code = e.keyCode || e.which;
if (code == TAB_KEY) {
e.preventDefault();
let parent = e.target;
let ul = document.createElement('ul');
let li = document.createElement('li');
ul.appendChild(li);
parent.appendChild(ul);
moveCursorToEnd(li);
} else if (code == ENTER_KEY) {
e.preventDefault(); // Stop treating it like an actual line feed
let parent = e.target;
let li = document.createElement('li');
parent.appendChild(li);
moveCursorToEnd(li);
}
});
function moveCursorToEnd(el) {
el.focus();
document.execCommand('selectAll', false, null);
document.getSelection().collapseToEnd();
}
I commented out all of your main code and put an alert in the event listener to find out what was going on. It turns out it was sending the error cannot use property addEventListener of undefined as it did not like window.editor.
The solution was to replace window.editor with just editor.
Try typing in your contenteditable div in the snippet below to see how it works when you use editor instead. This is probably because you declared the editor variable with let instead of var, which affects the scope of the variable in a different way so it does not become a property of the window object.
const TAB_KEY = 9;
const ENTER_KEY = 13;
let editor = document.querySelector('.editor');
editor.appendChild(document.createElement('li')); // Add initial (for visibility)
/*editor.addEventListener('focus', (e) => {
//let ul = e.target;
//let li = document.createElement('li');
//ul.appendChild(li);
});*/
editor.addEventListener('keydown', (e) => {
/*let code = e.keyCode || e.which;
if (code == TAB_KEY) {
e.preventDefault();
let parent = e.target;
let ul = document.createElement('ul');
let li = document.createElement('li');
ul.appendChild(li);
parent.appendChild(ul);
moveCursorToEnd(li);
} else if (code == ENTER_KEY) {
e.preventDefault(); // Stop treating it like an actual line feed
let parent = e.target;
let li = document.createElement('li');
parent.appendChild(li);
moveCursorToEnd(li);
}*/
alert("Works");
});
<div>
<input type="button" onclick="reset();" value="Reset form">
<button id="pr" onclick="reset()"> Clear </button>
<div id="ul" class="editor" contenteditable="true" draggable="true" ></div>
</div>
Related
I'm working on a simple todo list app in JavaScript.
I need to add new items from the input field on a list, and have the ability to remove items from the list.
I wrote all the necessary functions for storing values from input field and adding it to a list. However I am not able to make removing elements work.
HTML, CSS and JS are provided. removeItems() function doesn't work.
const enterBtn = document.getElementById('enter');
const input = document.querySelector('input');
const ulList = document.querySelector('ul');
const delBtn = document.querySelector('button');
// Get input from the input field and create a new li itemn in ul list
function inputLength() {
return input.value.length;
}
function addDelButtonsToExistingItems() {
const liItems = document.querySelectorAll('li');
for (let i = 0; i < liItems.length; i++) {
const delBtn = document.createElement('button');
delBtn.appendChild(document.createTextNode('delete'));
liItems[i].appendChild(delBtn);
}
}
// Add delete buttons to already existing items in a document
addDelButtonsToExistingItems();
// to add new items from the input field
function addNewItem() {
if (inputLength() > 0) {
const newLiItem = document.createElement('li');
newLiItem.appendChild(document.createTextNode(input.value));
const delBtn = document.createElement('button');
delBtn.appendChild(document.createTextNode('delete'));
newLiItem.appendChild(delBtn);
ulList.appendChild(newLiItem);
input.value = '';
}
}
/**
* To remove items from the list on delete btn pressed
*/
function removeItems(e) {
if (e.target.tagName === 'BUTTON') {
e.target.classList.toggle('remove');
}
}
/**
* To toggle done class on list items clicked
*/
function toggleDone(e) {
if (e.target.tagName === 'LI') {
e.target.classList.toggle('done');
}
}
// If enter key is pressed
function addOnEnterKeyPressed(e) {
if (e.keyCode === 13) {
addNewItem();
}
}
// To add new items on enter btn clicked
enterBtn.addEventListener('click', addNewItem);
// To add new items on enter key pressed
input.addEventListener('keypress', addOnEnterKeyPressed);
// To toggle the done class
ulList.addEventListener('click', toggleDone);
// To remove the items on del btn click
// ???????
.done{
text-decoration: line-through;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Shipping List</title>
</head>
<body>
<h1>Shopping List</h1>
<h3>Get it done today</h3>
<input type="text" name="input" id="input">
<button id="enter">Enter</button>
<ul>
<li>NoteBook</li>
<li>Pens</li>
<li>Eraser</li>
<li>CMPSC 412 book</li>
</ul>
<script src="script.js"></script>
</body>
</html>
Updated the code a bit to get rid of some of the const and simplified the way the delete button click event target is found.
Mostly, I've added a class delete to the delete buttons, so we can target those specifically without relying on the tag.
I commented the code I changed.
const enterBtn = document.getElementById('enter');
const input = document.querySelector('input');
const ulList = document.querySelector('ul');
// Get input from the input field and create a new li itemn in ul list
function inputLength() {
return input.value.length;
}
function addDelButtonsToExistingItems() {
const liItems = document.querySelectorAll('li');
for (let i = 0; i < liItems.length; i++) {
const delBtn = document.createElement('button');
// Let's add a class so we can target it instead of using a tag name.
delBtn.classList.add('delete');
delBtn.appendChild(document.createTextNode('delete'));
liItems[i].appendChild(delBtn);
}
}
// Add delete buttons to already existing items in a document
addDelButtonsToExistingItems();
// to add new items from the input field
function addNewItem() {
if (inputLength() > 0) {
const newLiItem = document.createElement('li');
newLiItem.appendChild(document.createTextNode(input.value));
const delBtn = document.createElement('button');
// Adding a class again.
delBtn.classList.add('delete');
delBtn.appendChild(document.createTextNode('delete'));
newLiItem.appendChild(delBtn);
ulList.appendChild(newLiItem);
input.value = '';
}
}
/**
* To remove items from the list on delete btn pressed. We will do this looking at the document.
*/
function removeItem(e) {
// Since we are watching the document for a click, we can see if the target of the event has the class "delete" and we can remove its parent li.
if (e.target.classList.contains('delete')) {
e.target.parentNode.remove();
}
}
/**
* To toggle done class on list items clicked
*/
function toggleDone(e) {
if (e.target.tagName === 'LI') {
e.target.classList.toggle('done');
}
}
// If enter key is pressed
function addOnEnterKeyPressed(e) {
if (e.keyCode === 13) {
addNewItem();
}
}
// To add new items on enter btn clicked
enterBtn.addEventListener('click', addNewItem);
// To add new items on enter key pressed
input.addEventListener('keypress', addOnEnterKeyPressed);
// To toggle the done class
ulList.addEventListener('click', toggleDone);
// To remove the items on del btn click. We are targeting the document, since the delete elements can update.
document.addEventListener('click', removeItem);
.done {
text-decoration: line-through;
}
<h1>Shopping List</h1>
<h3>Get it done today</h3>
<input type="text" name="input" id="input">
<button id="enter">Enter</button>
<ul>
<li>NoteBook</li>
<li>Pens</li>
<li>Eraser</li>
<li>CMPSC 412 book</li>
</ul>
I am an absolute beginner in this field. And what i m doing is a small todo-list page.
And I am stuck in the following 'button events' part.
As in the code, I want to toggle the class of 'completed' to the ancestor div of the button clicked. However, it seems to work only for some elements.
Meaning, if the nodelist of 'finishBtn' variable has an odd number length, the class list only toggles for even number indexes and vice versa.
For example, if nodelist.length = 3, then the class list only toggles for nodelist[0]
and nodelist[2].
(However, for removeBtn variable, it works just fine)
Thank you very much for your time. I would really appreciate every reply of yours.
Cos I m stuck in this bloody thing for hours.
addBtn.addEventListener('click', (e)=> {
e.preventDefault();
if(input.value === ''){
return;
}
// adding elements to the body;
const eachTodo = document.createElement('div');
eachTodo.classList.add('eachTodo');
const textName = document.createElement('p');
textName.textContent = input.value;
const btns = document.createElement('div');
btns.classList.add('btns');
const finish = document.createElement('button');
finish.classList.add('finish');
finish.textContent = 'Finish';
const remove = document.createElement('button');
remove.classList.add('remove');
remove.textContent = 'Remove';
btns.append(finish, remove);
eachTodo.append(textName, btns);
plansDiv.append(eachTodo);
input.value = '';
//button Events
const finishBtn = document.querySelectorAll('.finish');
const removeBtn = document.querySelectorAll('.remove');
finishBtn.forEach(btn => {
btn.addEventListener('click', ()=>{
btn.parentElement.parentElement.classList.toggle('completed');
})
})
removeBtn.forEach(btn => {
btn.addEventListener('click', ()=>{
btn.parentElement.parentElement.remove();
})
})
})
This is my CSS part
.completed p {
text-decoration: line-through;
opacity: 0.8;
}
As it stands you're querying all buttons on each add and adding new listeners to them all resulting in duplicate listeners on each button that fire sequentially.
Elements with an even number of listeners attached will toggle the class an even number of times and return to the initial value.
"on" toggle-> "off" toggle-> "on"
Elements with an odd number of attached listeners toggle the class an odd number of times and appear correct at the end.
"on" toggle-> "off" toggle-> "on" toggle-> "off"
(It works for Remove because the first listener removes the element and subsequent Removes don't change that.)
Add listeners only once to each button
You can avoid this by simply adding the listeners directly to the newly created buttons. You already have references to each button and their parent element (eachTodo) in the script, so you can just add the listeners directly to them and reference the parent directly.
finish.addEventListener('click', () => {
eachTodo.classList.toggle('completed');
});
remove.addEventListener('click', () => {
eachTodo.remove();
});
const addBtn = document.getElementById('addBtn');
const plansDiv = document.getElementById('plansDiv');
const input = document.getElementById('input');
addBtn.addEventListener('click', (e) => {
e.preventDefault();
if (input.value === '') {
return;
}
// adding elements to the body;
const eachTodo = document.createElement('div');
eachTodo.classList.add('eachTodo');
const textName = document.createElement('p');
textName.textContent = input.value;
const btns = document.createElement('div');
btns.classList.add('btns');
const finish = document.createElement('button');
finish.classList.add('finish');
finish.textContent = 'Finish';
const remove = document.createElement('button');
remove.classList.add('remove');
remove.textContent = 'Remove';
btns.append(finish, remove);
eachTodo.append(textName, btns);
plansDiv.append(eachTodo);
input.value = '';
// Add listeners directly
finish.addEventListener('click', () => {
eachTodo.classList.toggle('completed');
})
remove.addEventListener('click', () => {
eachTodo.remove();
})
})
.completed p {
text-decoration: line-through;
opacity: 0.8;
}
<input type="text" id="input">
<button type="button" id="addBtn">Add</button>
<div id="plansDiv"></div>
Event delegation
A more concise solution would be to use event delegation and handle all the buttons in a single handler added to the document. Here replacing parentElement with closest('.eachTodo') to avoid the fragility of a specific ancestor depth, and checking which button was clicked using Element.matches().
document.addEventListener('click', (e) => {
if (e.target.matches('button.finish')){
e.target.closest('.eachTodo').classList.toggle('completed');
}
if (e.target.matches('button.remove')){
e.target.closest('.eachTodo').remove();
}
});
const addBtn = document.getElementById('addBtn');
const plansDiv = document.getElementById('plansDiv');
const input = document.getElementById('input');
document.addEventListener('click', (e) => {
if (e.target.matches('button.finish')){
e.target.closest('.eachTodo').classList.toggle('completed');
}
if (e.target.matches('button.remove')){
e.target.closest('.eachTodo').remove();
}
});
addBtn.addEventListener('click', (e) => {
if (input.value === '') {
return;
}
// adding elements to the body;
const eachTodo = document.createElement('div');
eachTodo.classList.add('eachTodo');
const textName = document.createElement('p');
textName.textContent = input.value;
const btns = document.createElement('div');
btns.classList.add('btns');
const finish = document.createElement('button');
finish.classList.add('finish');
finish.textContent = 'Finish';
finish.type = 'button';
const remove = document.createElement('button');
remove.classList.add('remove');
remove.textContent = 'Remove';
remove.type = 'button';
btns.append(finish, remove);
eachTodo.append(textName, btns);
plansDiv.append(eachTodo);
input.value = '';
});
.completed p {
text-decoration: line-through;
opacity: 0.8;
}
<input type="text" id="input">
<button type="button" id="addBtn">Add</button>
<div id="plansDiv"></div>
Note that you can also avoid the necessity of e.preventDefault() by specifying type="button" when you create your buttons. see: The Button element: type
I'm fairly new to JavaScript, so looking for the simplest solution possible (also, not looking for a jQuery solution).
I'm trying to add clicked list items to a new list, only if the clicked list item isn't already in the new list. I have managed to add the clicked list item to the new list, but I fail to create a functioning if statement that checks the new list for the current item.
window.onload = function () {
var ul = document.getElementById('bulk');
ul.addEventListener('click', function (e) {
var target = e.target;
while (target && target.parentNode !== ul) {
target = target.parentNode;
if(!target) { return; }
}
if (target.tagName === 'LI'){
var node = document.createElement("LI");
var textnode = document.createTextNode(target.id);
var textarea = document.getElementById("test1");
if (!textarea.querySelector(target.id)) {
node.appendChild(textnode);
document.getElementById("test1").appendChild(node);
}
}
});
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="test.js"></script>
</head>
<body>
<ul id="bulk">
<li id="banana"><b>banana</b></li>
<li id="apple"><b>apple</b></li>
</ul>
<ul id="test1"></ul><br>
</body>
</html>
Since you're not supposed to have multiple elements with the same ID in a single document, I changed things so the unique ID of each element is in a dataset attribute data-id.
Beyond that, it's just a question of using querySelector() to figure out whether such a node already is in the destination list.
var ul = document.getElementById('bulk');
var destination = document.getElementById("test1");
ul.addEventListener('click', function(e) {
var target = e.target;
while (target && target.parentNode !== ul) {
target = target.parentNode;
if (!target) {
return;
}
}
if (target.tagName === 'LI') {
var id = target.dataset.id;
var node = document.createElement("LI");
node.dataset.id = id;
var textnode = document.createTextNode("element with id " + id);
if (destination.querySelector("[data-id=" + id + "]")) {
return;
}
node.appendChild(textnode);
destination.appendChild(node);
}
});
<ul id="bulk">
<li data-id="banana"><b>banana</b></li>
<li data-id="apple"><b>apple</b></li>
</ul>
<hr />
<ul id="test1">
</ul>
I want to add a delete button beside each of the items that are to be added.
How to do this properly so the all the functions work?
I have tried the method below as you will see in the code. This seems correct to me but it's not working. This needs to be purely JavaScript`.
var button = document.createElement("BUTTON");
var ul = document.getElementById("list");
var li = document.createElement("li");
function handleAddNewItem() //adds new items and more
{
var item = document.getElementById("input").value;
var ul = document.getElementById("list");
var li = document.createElement("li");
if (item === '') {
alert("Input field can not be empty");
}
else {
button.innerText = "Delete";
li.appendChild(document.createTextNode("- " + item));
ul.appendChild(li);
ul.appendChild(button);
}
document.getElementById("input").value = ""; //clears input
//li.onclick = clearDom;
}//code deletes items by clearDom function
document.body.onkeyup = function (e) //allows items to be added with enter button
{
if (e.keyCode == 13) {
handleAddNewItem();
}
}
function clearDom() {
//e.target.parentElement.removeChild(e.target);//removeChild used
ul.removeChild(li);
ul.removeChild(button);
}
button.addEventListener("click", clearDom);
<body>
<input id="input" placeholder="What needs to be done?">
<button id="add_button" onclick="handleAddNewItem()">ADD</button>
<ul id="list">
</ul>
</body>
<script src="new.js"></script>
</html>
var button = document.createElement("BUTTON");
var ul = document.getElementById("list");
var li = document.createElement("li");
function handleAddNewItem() //adds new items and more
{
var item = document.getElementById("input").value;
var ul = document.getElementById("list");
var li = document.createElement("li");
if (item === '') {
alert("Input field can not be empty");
} else {
button.innerText = "Delete";
li.appendChild(document.createTextNode("- " + item));
ul.appendChild(li);
ul.appendChild(button);
}
document.getElementById("input").value = ""; //clears input
//li.onclick = clearDom;
} //code deletes items by clearDom function
document.body.onkeyup = function(e) //allows items to be added with enter button
{
if (e.keyCode == 13) {
handleAddNewItem();
}
}
function clearDom() {
//e.target.parentElement.removeChild(e.target);//removeChild used
ul.removeChild(li);
ul.removeChild(button);
}
button.addEventListener("click", clearDom);
<input id="input" placeholder="What needs to be done?">
<button id="add_button" onclick="handleAddNewItem()">ADD</button>
<ul id="list">
</ul>
<!-- commented out to reduce errors in the console
<script src="new.js"></script> -->
I am facing this error for now-
"The node to be removed is not a child of this node. at
HTMLButtonElement.clearDom new.js:33:7"
I want to implement the delete button in line with the items listed. so that it deletes the items added one by one separately.
I'd suggest:
function handleAddNewItem() {
/* Move the creation of all variables within the function
in which they're being used: */
const button = document.createElement('button'),
ul = document.getElementById('list'),
li = document.createElement('li'),
item = document.getElementById('input').value;
// here we use String.prototype.trim() to remove leading
// and trailing whitespace from the entered value, to
// prevent a string of white-space (' ') being considered
// valid:
if (item.trim() === '') {
alert("Input field can not be empty");
} else {
button.textContent = "Delete";
// here we again use String.prototype.trim(), this time to
// avoid the creation of a ' task '
// with extraneous white-space:
li.appendChild(document.createTextNode("- " + item.trim()));
// appending the <button> to the <li> instead
// of the <ul> (of which it would be an invalid
// child element anyway):
li.appendChild(button);
ul.appendChild(li);
}
document.getElementById("input").value = ''; //clears input
}
document.body.onkeyup = function(e) //allows items to be added with enter button
{
if (e.keyCode == 13) {
handleAddNewItem();
}
}
// the e - the EventObject - is passed automagically from
// the later use of EventTarget.addEventListener():
function clearDom(e) {
// e.target is the element on which the event that we're
// reacting to was originally fired (the <button>):
const clickedButton = e.target;
// here we use DOM traversal methods to find the closest
// ancestor <li> element, and then use ChildNode.remove()
// to remove it from the DOM:
clickedButton.closest('li').remove();
}
// using event-delegation to catch the
// delete-button clicks:
// first we retrieve the element already on the page which
// will be an ancestor of the appended elements:
document.getElementById('list')
// we then bind the clearDom() function - note the deliberate
// lack of parentheses - as the 'click' event-handler:
.addEventListener('click', clearDom);
function handleAddNewItem() {
/* Creating all variables within the function: */
const button = document.createElement('button'),
ul = document.getElementById('list'),
li = document.createElement('li'),
item = document.getElementById('input').value;
if (item.trim() === '') {
alert("Input field can not be empty");
} else {
button.textContent = "Delete";
li.appendChild(document.createTextNode("- " + item));
li.appendChild(button);
ul.appendChild(li);
}
document.getElementById("input").value = '';
}
document.body.onkeyup = function(e) {
if (e.keyCode == 13) {
handleAddNewItem();
}
}
function clearDom(e) {
const clickedButton = e.target;
clickedButton.closest('li').remove();
}
document.getElementById('list')
.addEventListener('click', clearDom);
<input id="input" placeholder="What needs to be done?">
<button id="add_button" onclick="handleAddNewItem()">ADD</button>
<ul id="list">
</ul>
While this question is already, arguably, already answered, I had a few moments to spare and took advantage of this question to begin learning how to use custom elements. The code, as above, is explained so far as possible using comments in the code itself:
// using an Immediately-Invoked Function
// Expression ('IIFE') to handle the creation of the
// custom element:
(function() {
// creating an HTML <template> element, this could
// instead be placed in, and retrieved from, the DOM:
const template = document.createElement('template');
// using a template literal to create, and format
// the HTML of the created <template> (using a template
// literal allows for new-lines and indentation):
template.innerHTML = `
<style>
*, ::before, ::after {
padding: 0;
margin: 0;
box-sizing: border-box;
}
div.layout {
display: grid;
grid-template-columns: 1fr min-content;
}
div.buttonWrap {
display: flex;
flex-direction: column;
align-items: flex-start;
}
</style>
<div class="layout">
<p></p>
<div class="buttonWrap">
<button>delete</button>
</div>
</div>
`;
// using class syntax:
class TaskItem extends HTMLElement {
// the constructor for the class and, by extension,
// the element that we're defining/creating:
constructor() {
// it seems that super() must be placed as the
// first thing in the constructor function:
super();
// we're holding the contents of the custom
// element in the Shadow DOM, to avoid its
// descendants being affected by CSS in the
// parent page and to prevent JavaScript in
// the document from interacting with the
// contents:
this.attachShadow({
// we want to interact and use elements in
// the Shadow Root, so it must be 'open'
// (although 'closed' is the other valid
// mode-type:
mode: 'open'
});
// here we append the content - not the node
// itself - of the created <template> element
// using Node.cloneNode(), the Boolean true
// means that the descendant elements are also
// cloned and therefore appended:
this.shadowRoot.appendChild(
template.content.cloneNode(true)
);
// for easier reading we cache the shadowRoot
// here (otherwise line-lengths can be a bit
// silly):
const root = this.shadowRoot,
// retrieving the <button> element, which will
// handle the task deletion:
del = root.querySelector('button');
// binding the anonymous function - defined
// using an Arrow function as we don't
// want to change the 'this' in the function -
// as the event-handler for the 'click' event:
del.addEventListener('click', () =>
// here we traverse to the parentNode of
// the 'this', and then use
// parentNode.removeChild() to remove the
// 'this' node:
this.parentNode.removeChild(this));
}
// this callback is executed when the element is
// connected/attached to the DOM:
connectedCallback() {
// we find the Shadow Root:
this.shadowRoot
// find the descendent <p> element:
.querySelector('p')
// and set its text-content to be equal
// to that of the data-task attribute:
.textContent = this.dataset.task;
}
}
// here we define the custom element and its
// class:
window.customElements.define('task-item', TaskItem);
})();
// here we cache a reference to the <button> which will
// cause the addition of new tasks:
const addTask = document.getElementById('add_button'),
// define the function that will handle the
// addition of new tasks:
createTask = () => {
// caching the <input> element:
const taskSource = document.getElementById('input'),
// retrieving and trimming the entered
// <input> value:
task = taskSource.value.trim(),
// creating a new element (custom
// elements are created the same way
// as 'normal' elements):
createdTask = document.createElement('task-item');
// updating the data-task attribute, for
// retrieval/use later when the element
// is added to the DOM:
createdTask.dataset.task = task;
// if we have a task (a zero-length/empty
// string is considered falsey, a string
// with a length greater than zero is
// considered truthy and string with negative
// length is considered impossible (I think),
// and therefore falsey:
if (task) {
// we retrieve the element holding the
// <task-item> elements:
document.getElementById('list')
// and append the created element:
.appendChild(createdTask);
}
// removing the <input> element's value:
taskSource.value = '';
};
// adding createTask() as the event-handler for
// the 'click' event on the <button>:
addTask.addEventListener('click', createTask);
// binding an anonymous function as the handler for
// keyup events on the <body> (binding to a closer
// ancestor would be more sensible in production):
document.body.addEventListener('keyup', (e) => {
// if the e.which is 13 we trust that to be the
// enter key, and then we call createTask()
if (e.which === 13) {
createTask();
}
})
#list {
margin-top: 0.5em;
min-height: 1.5em;
background: transparent radial-gradient(at 0 0, skyblue, lime);
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-gap: 5px;
}
#list:empty::before {
content: 'Add a new task!';
background: transparent linear-gradient(to right, #fffa, #fff0);
padding: 0 0 0 1em;
}
task-item {
border: 2px solid lime;
padding: 0.25em;
background-color: #fff9;
}
<input id="input" class="add_task" placeholder="What needs to be done?">
<button id="add_button" class="add_task">ADD</button>
<div id="list"></div>
JS Fiddle demo.
ChildNode.remove().
Classes.
Constructor.
document.createElement().
document.getElementById().
document.querySelector().
Element.attachShadow().
Event object.
event.target.
EventTarget.addEventListener().
Node.appendChild().
Node.parentNode.
Node.removeChild().
Node.textContent.
super().
Window.customElements.
Not very nice but a solution :)
else {
button.innerText = 'Delete';
li.appendChild(document.createTextNode('- ' + item));
ul.appendChild(li);
let but = button.cloneNode(true); // <-- solution
li.appendChild(but);
// clearDom function
clearDom();
}
And also a function that erases a single entry
function clearDom() {
let buttons = document.querySelectorAll('button:not(#add_button)');
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function (e) {
e.target.parentNode.remove();
}, false);
}
}
let button = document.createElement('button');
let ul = document.getElementById('list');
let li = document.createElement('li');
function handleAddNewItem() {
let item = document.getElementById('input').value;
let ul = document.getElementById('list');
let li = document.createElement('li');
if (item === '') {
alert('Input field can not be empty');
}
else {
button.innerText = 'Delete';
li.appendChild(document.createTextNode('- ' + item));
ul.appendChild(li);
let but = button.cloneNode(true);
li.appendChild(but);
clearDom();
}
document.getElementById('input').value = ''; // clears input
}
function clearDom() {
let buttons = document.querySelectorAll('button:not(#add_button)');
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function (e) {
e.target.parentNode.remove();
}, false);
}
}
document.body.onkeyup = function (e) {
if (e.keyCode === 13) {
handleAddNewItem();
}
};
<input id="input" placeholder="What needs to be done?">
<button id="add_button" onclick="handleAddNewItem()">ADD</button>
<ul id="list"></ul>
I am creating a to do list project in vanilla Javascript and have been trying to figure out how to edit an li item in the list on a double click mouse event. I want to be able to have the original text in the textbox after double clicking and update with the new text after clicking away from the textbox or pressing enter.
This is something I tried to write. Please let me know what I am doing wrong or if there is a better approach to the problem.
editInput function() {
var todosUl = document.querySelector('ul');
todosUl.addEventListener('dblclick', function(event) {
if (event.target.childNodes[0]) {
var originalInput = event.target.childNodes[0];
var editingInput = document.createElement("input");
editingInput.type = 'text';
parent = editingInput.parentNode;
parent.replaceChild(originalInput, editingInput);
}
Please no jQuery!
you can use contenteditabe property of html, and add the property on click to the desired element:
Object.prototype.insertAfter = function (newNode) {
this.parentNode.insertBefore(newNode, this.nextSibling);
};
document.addEventListener('keypress', function(e) {
if(e.currentTarget.activeElement.className === 'content'){
if ((e.keyCode || e.which) == 13) {
var li = document.createElement('li');
var ce = document.createAttribute('contenteditable');
ce.value = "true";
var cl = document.createAttribute('class');
cl.value='content';
li.setAttributeNode(ce);
li.setAttributeNode(cl);
e.currentTarget.activeElement.insertAfter(li);
li.focus();
return false;
}else{
return true;
}
}
});
li {
border: 1px solid #ccc;
}
<ul>
<li contenteditable="true" class="content">
</li>