I have a to-do list which I want to be able to cross out tasks as they're completed, as well as delete them by clicking their corresponding "X" button.
I use global variable row which is added to different elements' ID. This is how I differentiate them from other task entries.
This works fine when deleting a task, but not when the checkbox is checked/unchecked. I want to toggle the add-strikethrough CSS class whenever a task's checkbox is clicked.
Relevant HTML:
<body>
<h1>To-Do-List</h1>
<form action="" onsubmit="createElements();return false" id="todo" autocomplete=off>
<input type="text" maxlength="25" id="enter-task" placeholder="Enter a Task">
<button type="button" id="submit-task" onClick="createElements()"><span>+</span></button>
</form>
<h4 class="hide" id="task-header">current tasks</h4>
<form class="hide" id="container">
<!--This is where the addTask elements go-->
</form>
<script src="script.js"></script>
</body>
Relevant CSS:
.add-strikethrough{
text-decoration: line-through;
text-decoration-color: black;
}
Javascript:
var row = 0;
function createElements(){
//create Checkbox
var checkbox = document.createElement("input");
checkbox.setAttribute("type", "checkbox");
checkbox.setAttribute("id", "entryCheckbox");
checkbox.setAttribute("value", "unchecked");
checkbox.setAttribute("onClick", "strikeTask(" + row +");")
//create label using value from input box
var label = document.createElement("label");
label.setAttribute("id", "entryLabel"+row);
label = document.getElementById("enter-task").value;
//create deleteTask img
var deleteTask = document.createElement("img");
deleteTask.src = "images/x.svg";
deleteTask.setAttribute("id", "delete-task");
deleteTask.setAttribute("onClick", "deleteRow(" + row +");")
addTask(checkbox, label, deleteTask);
}
//This function appends the elements
function addTask(box, label, x){
const tasks = document.getElementById("container");
const newTask = document.createElement('div');
newTask.classList.add('task-entries');
const header = document.getElementById('task-header');
//Makes sure input field is not empty
if(label !== ""){
//Show task container and header
if(tasks.classList.contains("hide")){
tasks.classList.replace('hide', 'task-container');
header.classList.replace('hide', 'show-header')
};
//Append newTask to tasks container
tasks.appendChild(newTask);
newTask.setAttribute("id", "contentDiv"+row);
newTask.appendChild(box);
newTask.appendChild(document.createTextNode(label));
newTask.appendChild(x);
row++;
//Resets input field after task is added
document.getElementById("enter-task").value = null;
}
}
function deleteRow(ID){
document.getElementById('contentDiv'+ID).remove();
tasks = document.getElementById('container');
header = document.getElementById('task-header');
//Hide header and task container if no tasks exist
if(tasks.children.length == 0){
tasks.classList.replace('task-container', 'hide');
header.classList.replace('show-header', 'hide');
}
}
function strikeTask(ID){
const x = document.getElementById('entryLabel'+ID);
x.classList.toggle('add-strikethrough');
}
The strikeTask function is where my problem is; I can't figure out how to apply the class to the label when I click the corresponding checkbox
I appreciate any help!
One way to check what is wrong is to inspect the element created. If you do that you will notice that you don't have a label within a div, only text within a div, because you create a text node with the value of the label, as seen here:
You cannot apply a class to a text node. So you have to create the label element, add text to the label element, then append it.
Here is updated Javascript that will do that:
var row = 0;
function createElements(){
//create Checkbox
var checkbox = document.createElement("input");
checkbox.setAttribute("type", "checkbox");
checkbox.setAttribute("id", "entryCheckbox");
checkbox.setAttribute("value", "unchecked");
checkbox.setAttribute("onClick", "strikeTask(" + row +");")
//create label using value from input box
var label = document.createElement("label");
label.setAttribute("id", "entryLabel"+row);
label.textContent = document.getElementById("enter-task").value;
//create deleteTask img
var deleteTask = document.createElement("img");
deleteTask.src = "images/x.svg";
deleteTask.setAttribute("id", "delete-task");
deleteTask.setAttribute("onClick", "deleteRow(" + row +");")
addTask(checkbox, label, deleteTask);
}
//This function appends the elements
function addTask(box, label, x){
const tasks = document.getElementById("container");
const newTask = document.createElement('div');
newTask.classList.add('task-entries');
const header = document.getElementById('task-header');
//Makes sure input field is not empty
if(label !== ""){
//Show task container and header
if(tasks.classList.contains("hide")){
tasks.classList.replace('hide', 'task-container');
header.classList.replace('hide', 'show-header')
};
//Append newTask to tasks container
tasks.appendChild(newTask);
newTask.setAttribute("id", "contentDiv"+row);
newTask.appendChild(box);
newTask.appendChild(label);
newTask.appendChild(x);
row++;
//Resets input field after task is added
document.getElementById("enter-task").value = null;
}
}
function deleteRow(ID){
document.getElementById('contentDiv'+ID).remove();
tasks = document.getElementById('container');
header = document.getElementById('task-header');
//Hide header and task container if no tasks exist
if(tasks.children.length == 0){
tasks.classList.replace('task-container', 'hide');
header.classList.replace('show-header', 'hide');
}
}
function strikeTask(ID){
const x = document.getElementById('entryLabel'+ID);
x.classList.toggle('add-strikethrough');
}
What I've changed: label = document.getElementById("enter-task").value; to label.textContent = document.getElementById("enter-task").value; inside createElements() & newTask.appendChild(document.createTextNode(label)); to newTask.appendChild(label); inside addTask(...).
The label is created as you were creating it; its text content is set to the value of the enter-task input; the element is appended.
What you were doing was create a label element and giving it an id, but on the following line (label = document.getElementById("enter-task").value;) changing the value of the label variable to a text, then adding that text as a text node (newTask.appendChild(document.createTextNode(label));).
Related
I am making a todo list, i have just about everything for it figured out but one area i am stuck on is my UL and Li.
Basically when you enter items into the list, you have the ability to click the checkbox beside said item when you complete the task, and it will put a line through the text.
But i also want it to move that item to the bottom of the list when it is clicked.
would anyone be able to help me with how i would go about doing that
code Below
// making event listener for adding item
let addBTN = document.getElementById('addBtn');
addBTN.addEventListener('click', addItem);
// this creates a new li based on the entered value in the text box that it gets when you hit the button
// Through Research found that setAttribute isn't really needed and i can just use .id , .type etc
function addItem() {
// Creating needed elements as well as getting text from textbox
let newLi = document.createElement("li");
let myLiValue = document.getElementById('textBoxAdd').value;
let liTextNode = document.createElement("label");
liTextNode.textContent = myLiValue;
// makes div for li
let newDivID = ('div_' + myLiValue);
let newDiv = document.createElement('div');
newDiv.id = newDivID;
// makes checkboxes for the li
let newCheckBoxID = ('checkbox_' + myLiValue);
let newCheckBox = document.createElement('input');
newCheckBox.type = 'checkbox';
newCheckBox.id = newCheckBoxID;
// makes delete button for the li
let newDeleteID = ('deleteButton_' + myLiValue);
let newDeleteButton = document.createElement("button")
newDeleteButton.type = 'button';
newDeleteButton.id = newDeleteID
newDeleteButton.textContent = 'Delete';
//newDeleteButton.setAttribute('onclick', 'deleteItem()');
newDeleteButton.innerHTML = 'Delete';
// appends it to my newDiv
newDiv.appendChild(newCheckBox);
newDiv.appendChild(liTextNode);
newDiv.appendChild(newDeleteButton);
// then appends my new div to the new Li
newLi.appendChild(newDiv);
// this just makes sure a user cant enter in a blank value
if (myLiValue == "") {
alert("Please Enter Something Before Hitting Add Item");
} else {
document.getElementById('theNewList').appendChild(newLi);
document.getElementById('textBoxAdd').value = "";
}
}
//creating event listener for checkbox line through text and moving item
let theList = document.getElementById('theNewList');
theList.addEventListener('click', checkedComplete);
// function that will target every check box in the list and if any get checked then it will add a line through the text
function checkedComplete(event) {
const checkboxElement = event.target;
if (checkboxElement.type === 'checkbox') {
if (checkboxElement.checked) {
checkboxElement.nextElementSibling.style.textDecoration = 'line-through';
// add in moving item
} else {
checkboxElement.nextElementSibling.style.textDecoration = 'none';
}
}
}
// adds deleteItem listener to the list
theList.addEventListener('click', deleteItem);
function deleteItem(event) {
const deleteButton = event.target;
if (deleteButton.type === 'button') {
const deleteParentNode = deleteButton.parentNode;
deleteParentNode.parentNode.removeChild(deleteParentNode);
}
}
You are going to have a storage of you todos, right? Even if you did not think about it, it can do all the work. Just create the array (you could use localStorage to prevent you data from disappearing after browser is restarted) containing your todos and their condition, like
const todos = [{todo:"watch the movie", completed: false}, {...}, {...}]
Now you can easily add or remove items with standard array methods pop&push, delete with splice, filter etc. After array is mdified, just update the page and build your list using Array.map.
You should just add the following logic where you have // add in moving item comment:
const theList = document.getElementById('theNewList');
const lastListItem = theList.children[theList.children.length - 1];
theList.insertBefore(lastListItem, checkboxElement.parentNode.parentNode);
We're selecting your ul and searching for its last li and then we're simply placing the li belonging to the checkboxElement after the last li.
Working example:
// making event listener for adding item
let addBTN = document.getElementById('addBtn');
addBTN.addEventListener('click', addItem);
// this creates a new li based on the entered value in the text box that it gets when you hit the button
// Through Research found that setAttribute isn't really needed and i can just use .id , .type etc
function addItem() {
// Creating needed elements as well as getting text from textbox
let newLi = document.createElement("li");
let myLiValue = document.getElementById('textBoxAdd').value;
let liTextNode = document.createElement("label");
liTextNode.textContent = myLiValue;
// makes div for li
let newDivID = ('div_' + myLiValue);
let newDiv = document.createElement('div');
newDiv.id = newDivID;
// makes checkboxes for the li
let newCheckBoxID = ('checkbox_' + myLiValue);
let newCheckBox = document.createElement('input');
newCheckBox.type = 'checkbox';
newCheckBox.id = newCheckBoxID;
// makes delete button for the li
let newDeleteID = ('deleteButton_' + myLiValue);
let newDeleteButton = document.createElement("button")
newDeleteButton.type = 'button';
newDeleteButton.id = newDeleteID
newDeleteButton.textContent = 'Delete';
//newDeleteButton.setAttribute('onclick', 'deleteItem()');
newDeleteButton.innerHTML = 'Delete';
// appends it to my newDiv
newDiv.appendChild(newCheckBox);
newDiv.appendChild(liTextNode);
newDiv.appendChild(newDeleteButton);
// then appends my new div to the new Li
newLi.appendChild(newDiv);
// this just makes sure a user cant enter in a blank value
if (myLiValue == "") {
alert("Please Enter Something Before Hitting Add Item");
} else {
document.getElementById('theNewList').appendChild(newLi);
document.getElementById('textBoxAdd').value = "";
}
}
//creating event listener for checkbox line through text and moving item
let theList = document.getElementById('theNewList');
theList.addEventListener('click', checkedComplete);
// function that will target every check box in the list and if any get checked then it will add a line through the text
function checkedComplete(event) {
const checkboxElement = event.target;
if (checkboxElement.type === 'checkbox') {
if (checkboxElement.checked) {
checkboxElement.nextElementSibling.style.textDecoration = 'line-through';
const theList = document.getElementById('theNewList');
const lastListItem = theList.children[theList.children.length - 1];
theList.insertBefore(checkboxElement.parentNode.parentNode, lastListItem.nextSilbing);
} else {
checkboxElement.nextElementSibling.style.textDecoration = 'none';
}
}
}
// adds deleteItem listener to the list
theList.addEventListener('click', deleteItem);
function deleteItem(event) {
const deleteButton = event.target;
if (deleteButton.type === 'button') {
const deleteParentNode = deleteButton.parentNode;
deleteParentNode.parentNode.removeChild(deleteParentNode);
}
}
<input id="textBoxAdd" type="text" />
<button id="addBtn" type="button">Add</button>
<ul id="theNewList"></ul>
Below is my code actually I wanted to set the li text into the input field when a user click on update button . The code is working input element is shown when user click on update button but the value which is in the li tag is not shown in the input filed but it is shown in the console I also attached the picture to clear this when I replace the input with a button
[function addTodo() {
// create li
var todo_input = document.getElementById('todo-input');
var li = document.createElement('li');
var textNode = document.createTextNode(todo_input.value);
li.setAttribute('class', 'li');
li.appendChild(textNode);
// create update button
var updateBtn = document.createElement('button');
var updateText = document.createTextNode('UPDATE');
li.appendChild(updateBtn);
li.appendChild(deleteBtn);
addList.appendChild(li);
}
function updateItem(e) {
var val1 = e.parentNode.firstChild;
var inputVal = document.createElement('input');
inputVal.setAttribute('class','todo-input');
inputVal.setAttribute('type','text');
var textNode = document.createTextNode(val1.nodeValue);
inputVal.appendChild(textNode);
}][1]
Don't append the text in the input box. Use the value attribute.
inputVal.value = val1.nodeValue;
I am trying to convert this HTML code to be generated by Javascript on the fly for live data.
<div class="dropdown">
<button class="dropbtn">Dropdown</button>
<div class="dropdown-content">
Link 1
Link 2
Link 3
</div>
</div>
Ive found a few methods like: appendChild, getElementById, innerHTML and so on. Here is what I've tried so far. I can't seem to get the data to show up.
stringy = data2.Items[0].groupName.values[i];
var para = document.createElement("div");
var node = document.createTextNode(stringy);
para.appendChild(node);
var element = document.getElementById("parental");
element.appendChild(para);
//create div and give it a class
para.setAttribute('class', 'dropbtn');
var div = document.createElement("div");
div.setAttribute('class', 'dropdown-content');
para.parentNode.insertBefore(div, para.nextSibling);
//create link tags and give them text
var alinky = document.createElement("a");
alinky.setAttribute('id', 'linky');
document.getElementById('linky').innerHTML = "linky poo"
div.appendChild(alinky);
Hopefully someone could fill in the blanks on getting this HTML code to be reproduced with javascript. Thanks in advance!
EDIT:
I am trying to create a dropdown menu like this:
https://www.w3schools.com/howto/tryit.asp?filename=tryhow_css_js_dropdown_hover
However, I am trying to create multiple dropdown menus, that dynamically change in quantity based on a query to DynamoDB (AWS). therefore I am using javascript to create the html tags.
The problem is that the scope of the query function does not allow me to see the data outside of the query function, or even inject data into it.
For example, if I try to get a button description from the query, and write to it descriptionArray[0] = data2.Items[0].description; so that I can append the button to the dropdown div, it doesn't know which iteration I'm on in the for loop due to scope. In this example, descriptionArray[0] will work, but descriptionArray[i] will not work because the for loop is outside the query.
Here is the entire logic:
//group data
var length = data2.Items[0].groupName.values.length;
// create elements
const dpdown1 = document.createElement('div');
// set dpdown1 class
dpdown1.setAttribute('class', 'dropdown');
console.log(dpdown1);
var button = new Array();
var dpdown2 = new Array();
var membersArray = new Array();
var descriptionArray = new Array();
var linksArray = new Array();
var stringy = new Array;
//list groups
for(i = 0; i<length; i++){
// create button, set button attribs
button[i] = document.createElement('button');
button[i].setAttribute('class','dropbtn');
//create dropdown div, set attributes
dpdown2[i] = document.createElement('div');
dpdown2[i].setAttribute('class', 'dropdown-content');
//list of group names
stringy[i] = data2.Items[0].groupName.values[i];
var stringyy = stringy[i];
var desc;
//query group members and description
var docClient1 = new AWS.DynamoDB.DocumentClient({ region: AWS.config.region });
var identityId = AWS.config.credentials.identityId;
var paramsyy = {
ExpressionAttributeValues: {
":v1": stringyy
},
KeyConditionExpression: "groupName = :v1",
TableName: "group"
};
docClient1.query(paramsyy, function(err, data2) {
if (err) {
console.error(err);
}else{
descriptionArray[0] = data2.Items[0].description;
//traverse members
for(k = 0; k<data2.Items[0].members.values.length; k++){
// create dropdown links of members
membersArray[k] = data2.Items[0].members.values[k];
linksArray[k] = document.createElement('a');
linksArray[k].setAttribute('href', '#')
linksArray[k].innerText = membersArray[k];
// nest into dpdown2 div, set dpdown2 attribs
dpdown2[0].appendChild(linksArray[k]);
}
}
});
button[i].innerText = stringyy + ": " + descriptionArray[0];
// nest into dpdown1
dpdown1.appendChild(button[i]);
dpdown1.appendChild(dpdown2[i]);
}
// append to DOM
const target = document.getElementById('target');
target.appendChild(dpdown1);
if I use the I from the first for loop inside the query function, it will give me undefined results.
here's how you can do it with vanilla JavaScipt, there are multiple ways to do it, but this way only uses 4 methods: createElement, setAttribute, appendChild, and getElementById, and directly sets 1 property: innerText.
// create elements
const dpdown1 = document.createElement('div');
const button = document.createElement('button');
const dpdown2 = document.createElement('div');
const link1 = document.createElement('a');
const link2 = document.createElement('a');
const link3 = document.createElement('a');
// set link attribs
link1.setAttribute('href', '#')
link1.innerText = 'Link 1';
link2.setAttribute('href', '#')
link2.innerText = 'Link 2';
link3.setAttribute('href', '#')
link3.innerText = 'Link 3';
// nest into dpdown2, set dpdown2 attribs
dpdown2.appendChild(link1);
dpdown2.appendChild(link2);
dpdown2.appendChild(link3);
dpdown2.setAttribute('class', 'dropdown-content');
// set button attribs
button.setAttribute('class','dropbtn');
button.innerText = "Dropdown"
// nest into dpdown1
dpdown1.appendChild(button);
dpdown1.appendChild(dpdown2);
// set dpdown1 class
dpdown1.setAttribute('class', 'dropdown');
// append to DOM
const target = document.getElementById('target');
target.appendChild(dpdown1);
<div id="target"></div>
You will to append it to something, in this example it's <div id="target"></div> but it could be something else.
Happy coding!
Mainly you are just doing things out of order.
Create the .dropdown <div> with its class.
Complete the .dropbtn <button> with its class and text.
Add the button to the div.
Create the .dropdown-content <div>.
Complete each link with its href attribute and text.
Add each link to the .dropdown-content <div>.
Add the .dropdown-content div to the .dropdown <div>.
Find the parent element in the document.
Append the whole complete .dropdown <div> to the document.
var para = document.createElement("div"); //make .dropdown div
para.setAttribute('class', 'dropdown'); //add .dropdown class to div
var button = document.createElement("button"); //create button
button.setAttribute('class', 'dropbtn'); //add .dropbtn class to button
var node = document.createTextNode('Dropdown'); //create button text
button.appendChild(node); //add text to button
para.appendChild(button); //add button to .dropdown div
var div = document.createElement("div"); //create .dropdown-content div
div.setAttribute('class', 'dropdown-content'); //add .dropdown-content class to div
//repeat for all necessary links
var alinky = document.createElement("a"); //creat link
alinky.setAttribute('href', '#'); //set link href attribute
var alinkyText = document.createTextNode("Link 1"); //create text for link
alinky.appendChild(alinkyText); //add text to link
div.appendChild(alinky); //add link to dropdown div
para.appendChild(div); //add .dropdown-content div to .dropdown div
var element = document.getElementById("parental"); //find parent element
element.parentNode.insertBefore(para, element.nextSibling); //add .dropdown div to the bottom of the parent element
<div id="parental">
</div>
// makes an li inside of a ol when a button is pressed
function addLi() {
var txtVal = document.getElementById('txtVal').value,
listNode = document.getElementById('list'),
liNode = document.createElement("li"),
txtNode = document.createTextNode(txtVal);
liNode.appendChild(txtNode);
listNode.appendChild(liNode);
};
function addoutp() {
};
I don't know what to put inside this function to move the inputted text over to another part of the screen separately...
You were very close. Just adding some HTML that matches your selectors in the JS code and it started working. I also added a check to make sure there was a value before adding to the list and also a reset to clear the input value after the text gets added to the list (txtVal.value = '';).
// makes an li inside of a ol when a button is pressed
function addLi() {
var txtVal = document.getElementById('txtVal'),
listNode = document.getElementById('list'),
liNode = document.createElement("li"),
txtNode = document.createTextNode(txtVal.value);
if (txtVal.value) {
liNode.appendChild(txtNode);
listNode.appendChild(liNode);
// clear the input
txtVal.value = '';
}
};
<input id="txtVal" type="text" />
<button onclick="addLi()">Add to list</button>
<ol id="list"></ol>
I want to take the user input text from this div and input tag I've got:
<input id="title" placeholder="Title (Optional)">
<div class="editor" contenteditable></div>
The div is a rich text field that I've put in place of a regular textarea tag
and create a list item inside a <ul> tag.
Here is the javascript I've got, but is not working...
(works just fine with regular text area, but I get nothing with the rich text form)
/*------POST SUBMIT JS------*/
//target all necessary HTML elements
var ul = document.getElementById('list'),
removeAll = document.getElementById('removeAll'),
add = document.getElementById('add');
//make something happen when clicking on 'submit'
add.onclick = function(){
addLi(ul)
};
//function for adding items
function addLi(targetUl){
var inputText = document.getElementsByClassName('editor').value, //grab input text (the new entry)
header = document.getElementById('title').value, //grab title text
li = document.createElement('li'), //create new entry/li inside ul
content = document.createElement('div'),
title = document.createElement('div'),
removeButton = document.createElement('button'); //create button to remove entries
content.setAttribute('class','content')
title.setAttribute('class','title')
content.innerHTML = inputText;
title.innerHTML = header;
if (inputText.split(' ').join(' ').length === 0) {
//check for empty inputs
alert ('No input');
return false;
}
removeButton.className = 'removeMe'; //add class to button for CSS
removeButton.innerHTML = 'Delete'; //add text to the remove button
removeButton.setAttribute('onclick', 'removeMe(this);'); //creates onclick event that triggers when entry is clicked
li.appendChild(title); //add title textnode to created li
li.appendChild(content); //add content textnode to created li
li.appendChild(removeButton); //add Remove button to created li
targetUl.appendChild(li); //add constructed li to the ul
}
//function to remove entries
function removeMe(item){
var deleteConfirm = confirm('Are you sure you want to delete this entry?');
if (deleteConfirm){var parent = item.parentElement;
parent.parentElement.removeChild(parent)}
};
function checkRemoval(){
var entryConfirm = confirm('Are you sure you want to delete all entries?');
if (entryConfirm){
ul.innerHTML = '';
}
};
Here is the demo I'm working on
Here is the demo using a textarea tag
getElementsByClassName('editor') is going to return an array of elements with the class editor, so you can't just do .value, you need to get the first element in the array.
Also, since it's a div, I think you want to use textContent, so it'll look like this
var inputText = document.getElementsByClassName('editor')[0].textContent
for the input type you have to write following into your javascript:
var input_val = getElementsByClassName('title').value;
for the div you have to write following into your javascript:
var div_val = getElementsByClass('editor').value;
I hope this will work