Im working on a checklist chrome app for myself and would love your help. Basically, I cant figure out how to save a checkbox status. If I check a box and refresh the page, I would like it to stay checked. But I cant seem to code that. Any ideas how to do that? Thank you!!
Edit: I added the html, it gives me the message that my post is mostly code and that I need to add some text, so here I am just writing some more to fulfil this requirement. There is no need reading this. Thanks for the help and sorry for the late edit
function get_todos() {
var todos = new Array;
var todos_str = localStorage.getItem('todo');
if (todos_str !== null) {
todos = JSON.parse(todos_str);
}
return todos;
}
function add() {
var task = document.getElementById('task').value;
var todos = get_todos();
todos.push(task);
localStorage.setItem('todo', JSON.stringify(todos));
show();
return false;
}
function remove() {
var id = this.getAttribute('id');
var todos = get_todos();
todos.splice(id, 1);
localStorage.setItem('todo', JSON.stringify(todos));
show();
return false;
}
function show() {
var todos = get_todos();
var html = '<ul>';
for(var i=0; i<todos.length; i++) {
html += '<li>' + '<input type="checkbox" id="checkbox">' + todos[i] + '<button class="remove" id="' + i + '">delete</button></li>' ;
};
html += '</ul>';
document.getElementById('todos').innerHTML = html;
var buttons = document.getElementsByClassName('remove');
for (var i=0; i < buttons.length; i++) {
buttons[i].addEventListener('click', remove);
};
}
document.getElementById('add').addEventListener('click', add);
show();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
</head>
<body>
<style>
html,body,h1,h3,h4,h6 {font-family: "Roboto";font-size: 24px; sans-serif}
h2 {font-family: "Roboto";font-size: 36px; sans-serif}
h5 {font-family: "Roboto";font-size: 28px; sans-serif}
</style>
<input id="task"><button id="add">Add</button>
<hr>
<div id="todos"></div>
<script src="todo.js"></script>
</body>
</html>
Seeing as no one else has provided an answer, I thought I'd throw in my potential solution, I went with a functional style because why not, it's simple, easy to read, etc...
PS
I included the fallback variable because on Stack Overflow, you can't access local storage when running a snippet.
let fallback = [];
const $e = query => document.querySelector(query);
// Return a todo list.
const getToDoList = () => {
let data = null;
try {
data = JSON.parse(localStorage.getItem('todo'));
} catch (e) {
data = fallback;
}
return data == null || Array.isArray(data) == false ? [] : data;
};
// Set the todo list.
const setToDoList = (data) => {
try {
localStorage.setItem('todo', JSON.stringify(data));
} catch (e) {
fallback = data;
}
};
// Add a task to the todo list.
const addToDo = () => {
const array = getToDoList();
array.push({value: $e("#task").value, checked: false});
setToDoList(array);
};
// Remove a task from the todo list.
const removeToDo = index => {
const array = getToDoList();
array.splice(index, 1);
setToDoList(array);
};
// Allow for the ability to remove an item from the todo list & other stuff..
const dispatchListEvents = () => {
document.querySelectorAll('#app ul li span').forEach(span => {
span.onclick = () => {
removeToDo(span.parentElement.getAttribute('data-index'));
render();
}
});
document.querySelectorAll('#app ul li input').forEach(input => {
input.onclick = () => {
const array = getToDoList();
const object = array[input.parentElement.getAttribute('data-index')];
object.checked = ! object.checked;
setToDoList(array);
render();
}
});
};
// Render the todo list.
const render = () => {
let index = 0;
const template = item => `<li data-index="${index++}">` +
`<input type="checkbox" ${item.checked ? 'checked' : ''} />` +
`${item.value} <span>X</span></li>`;
const re = new RegExp('</li>,', 'g'), replacement = '</li>';
const html = `<ul>${getToDoList().map(i => template(i))}</ul>`;
$e("#app").innerHTML = `${html.replace(re, replacement)}`;
dispatchListEvents();
};
// Allow the user to add a task to the todo list.
const addToListClickHandler = () => {
let result = $e("#task").value.replace(/\ /g, '').length > 0 ? addToDo() : null;
$e("#task").value = null; // Always null as addToDo returns null.
render();
};
// The function that will be fired when the DOM is ready.
const ready = () => {
render(); // Initial render.
$e("#addToList").addEventListener('click', addToListClickHandler);
};
// An insanely lazy implementation of $(document).ready.
const delay = 250;
const fakeOnReady = setTimeout(ready, delay);
body {
font-family: Arial, Helvetica, sans-serif;
}
#app ul li {
max-width: 150px;
}
#app ul li span {
float: right;
color: red;
}
#app ul li span:hover {
cursor: pointer;
}
<h1>To Do List</h1>
<section>
<h2>Add Task</h2>
<input id="task" placeholder="Task..."/>
<input type="button" id="addToList" value="Add"/>
</section>
<hr/>
<section>
<h2>Tasks</h2>
<div id="app">
<!-- Area for the DHTML. -->
</div>
</section>
Related
im building a to-do list but cant figure out how to keep my array values that have line-through decoration.
the moment render method is called, the array is built from the start. means that if i delete an li, all other li that have been marked by the checkbox with a line-through, losing the decoration.
what can i do to keep the line-through ?
i tried so far in the markTask method to replace the original value with the value that have line-through on it but it didn't work.
basically what im trying to accomplish is by inserting the value with line-through, to be able to check if this value have the line-through style and after the render to be able to keep the checked checkboxes as checked.
my code so far:
class Todo {
constructor() {
this.input = document.getElementById("input");
this.ul = document.getElementById("ul");
this.form = document.getElementById("form");
this.tasks = [];
this.registerEvent();
}
registerEvent() {
this.form.addEventListener("submit", (event) => {
event.preventDefault();
this.createTask(this.input.value);
this.form.reset();
});
}
createTask(task) {
if (task.trim().length === 0) {
return;
}
this.tasks.push(task);
this.render();
}
deleteTask(task) {
const myTask = task.target;
const parent = myTask.parentNode;
const taskToRemove = parent.childNodes[1].textContent;
const index = this.tasks.indexOf(taskToRemove);
this.tasks.splice(index, 1);
this.render();
}
markTask(task) {
const myTask = task.target;
const parent = myTask.parentNode;
if (myTask.checked) {
parent.style.textDecoration = "line-through";
} else {
parent.style.textDecoration = "none";
}
}
render() {
this.ul.innerHTML = "";
this.tasks.forEach((task) => {
const li = document.createElement("li");
const cb = document.createElement("input");
cb.type = "checkbox";
cb.addEventListener("click", (e) => {
this.markTask(e);
});
li.appendChild(cb);
li.append(document.createTextNode(task));
const btn = document.createElement("button");
li.appendChild(btn);
btn.textContent = "Delete";
btn.classList.add("remove");
btn.addEventListener("click", (e) => {
this.deleteTask(e);
});
this.ul.appendChild(li);
});
}
}
new Todo();
<form id="form">
<input id="input" />
<button id="add">Add</button>
</form>
<ul id="ul">
</ul>
it's because you're not tracking which tasks are done and you're just pushing strings. for your createTask method you need to push an object with a done property to indicate which tasks have been done like so
createTask(task) {
if (task.trim().length === 0) {
return;
}
this.tasks.push({title: task, done: false});
this.render();
}
update your render to account for tasks already done
render() {
this.ul.innerHTML = "";
this.tasks.forEach((task) => {
const li = document.createElement("li");
const cb = document.createElement("input");
cb.type = "checkbox";
cb.addEventListener("click", (e) => {
this.markTask(e);
});
li.appendChild(cb);
li.append(document.createTextNode(task.title));
const btn = document.createElement("button");
li.appendChild(btn);
btn.textContent = "Delete";
btn.classList.add("remove");
btn.addEventListener("click", (e) => {
this.deleteTask(e);
});
this.ul.appendChild(li);
if (task.done) {
cb.checked = true;
li.style.textDecoration = "line-through";
} else {
cb.checked = false;
li.style.textDecoration = "none";
}
});
}
in your constructor update your tasks variable to see this in effect
constructor() {
this.input = document.getElementById("input");
this.ul = document.getElementById("ul");
this.form = document.getElementById("form");
this.tasks = [{title: 'mill', done: true}, {title: 'jus', done: false}];
this.registerEvent();
}
hope you get the general idea. I won't do the entire implementation on markTask as this should be enough to give you a view of what the solution should be. good luck.
If I may, I have revised your code a bit.
The technique you need is event delegation:
any click on a child element is also a click on its parent elements. we plas the event listener on the parent and we see on which child element it occurred.
In your case, this only makes one event listerner for all your 'remove' buttons.
the other idea is not to ignore the DOM, it also keeps the list of tasks, you don't need to keep them in a table in memory, this is redundant.
here is the code: css is also helfull
class Todo
{
constructor()
{
this.form = document.getElementById('todo-form')
this.liste = document.getElementById('todo-list')
this.form.onsubmit = e => this.addTask(e)
this.liste.onclick = e => this.delTask(e)
}
addTask(e)
{
e.preventDefault()
if (this.form.task.value.trim() === '') return
let li = document.createElement('li')
, cb = document.createElement('input')
, sp = document.createElement('span')
, bt = document.createElement('button')
;
cb.type = 'checkbox'
sp.textContent = this.form.task.value
bt.textContent = 'Delete'
bt.className = 'remove'
li.appendChild(cb)
li.appendChild(sp)
li.appendChild(bt)
this.liste.appendChild(li)
this.form.reset()
}
delTask(e)
{
if (!e.target.matches('button.remove')) return // reject others clicks
e.target.closest('li').remove()
}
}
new Todo();
#todo-list li > span {
display : inline-block;
background-color : whitesmoke;
width : 20em;
}
#todo-list li input[type=checkbox]:checked + span {
text-decoration : line-through;
}
#todo-list li button.remove {
font-size: .6em;
}
<form id="todo-form">
<input name="task">
<button type="submit">Add</button>
</form>
<ul id="todo-list"></ul>
As you can see this code is shorter. You can also use a IIFE unstead of a class, like that :
(function() // IIFE
{
let form = document.getElementById('todo-form')
, liste = document.getElementById('todo-list')
;
form.onsubmit = e => // addTask
{
e.preventDefault()
if (form.task.value.trim() === '') return
let li = document.createElement('li')
, cb = document.createElement('input')
, sp = document.createElement('span')
, bt = document.createElement('button')
;
cb.type = 'checkbox'
sp.textContent = form.task.value
bt.textContent = 'Delete'
bt.className = 'remove'
li.appendChild(cb)
li.appendChild(sp)
li.appendChild(bt)
liste.appendChild(li)
form.reset()
}
liste.onclick = e => // delTask
{
if (!e.target.matches('button.remove')) return // reject others clicks
e.target.closest('li').remove()
}
}
)()
btTaskList.onclick = e =>
{
let tasks = [...document.querySelectorAll('#todo-list li')].map(li=>
{
let val = li.querySelector('span').textContent
, chk = li.querySelector('input[type=checkbox]').checked
;
return {val,chk}
})
console.clear()
console.log( tasks )
}
#todo-list li > span {
display : inline-block;
background-color : whitesmoke;
width : 20em;
}
#todo-list li input[type=checkbox]:checked + span {
text-decoration : line-through;
}
#todo-list li button.remove {
font-size: .6em;
}
<form id="todo-form">
<input name="task">
<button type="submit">Add</button>
</form>
<ul id="todo-list"></ul>
<button id="btTaskList">get task list</button>
I also added a get task list button...
After marking an element you are changing only the stayle and atrribute of element. But after delete you recreate with render whole list and in render you are not rendereing checked parameter.
Your render should be:
render() {
this.ul.innerHTML = "";
this.tasks.forEach((task) => {
const li = document.createElement("li");
const cb = document.createElement("input");
cb.type = "checkbox";
cb.addEventListener("click", (e) => {
this.markTask(e);
});
li.appendChild(cb);
// missed rendering checked
if (task.checked) {
li.style.textDecoration = "line-through";
cb.checked = 'checked';
}
li.append(document.createTextNode(task));
const btn = document.createElement("button");
li.appendChild(btn);
btn.textContent = "Delete";
btn.classList.add("remove");
btn.addEventListener("click", (e) => {
this.deleteTask(e);
});
this.ul.appendChild(li);
});
}
So I have a calculator with an error message that displays, if they press the "calculate" button if the input is NaN. The error message keeps getting created everytime the user presses calculate. How do i make it so it only shows even after pressing "calculate" multiple times?
function displayErr() {
const formBox = document.querySelector("form");
const errorBox = document.createElement("div");
errorBox.className = "errorBox";
const errorText = document.createTextNode("Those are not numbers!");
errorBox.appendChild(errorText);
formBox.appendChild(errorBox);
}
if ((isNaN(billInput)) || (isNaN(peopleAmount)) || (billInput === "") || (peopleAmount === "")) {
displayErr();
}
The most straightforward way is to check if the element already exists.
function displayErr() {
// Get error element
const errorElement = document.getElementsByClassName('errorBox');
// If it already exists
if (errorElement && errorElement.length > 0) {
// Dont add another one
return;
}
// Add new errorBox
const formBox = document.querySelector("form");
const errorBox = document.createElement("div");
errorBox.className = "errorBox";
const errorText = document.createTextNode("Those are not numbers!");
errorBox.appendChild(errorText);
formBox.appendChild(errorBox);
}
Another option would to be using css classes to 'hide' the element;
Always render the element, but hide it with display: none
In the displayErr(), make the element visible with something like document.getElementsByClassName('errorBox')[0].style.display = block;
a better way of doing this is
to show and hide the element using CSS classes
create the element and hide it using
display: none;
and show it by adding a class to the element
display: block;
const element = document.getElementById("myDIV");
const button = document.getElementById("btn");
button.addEventListener("click", () => element.classList.toggle("show"));
#myDIV {
display: none;
}
.show {
display: block !important;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<button id="btn">Try it</button>
<div id="myDIV">
This is a DIV element.
</div>
</body>
</html>
For what it's worth, here is a pure JavaScript example of a show/hide interpretation:
function CheckInput() {
const billInput = document.getElementById("b").value;
const peopleAmount = document.getElementById("p").value;
if ((isNaN(billInput)) || (isNaN(peopleAmount)) || (billInput === "") || (peopleAmount === "")) {
showErr();
}
else{
hideErr();
}
}
function hideErr(){
console.log("hide");
const el = document.getElementById("error");
el.style.display = "none";
}
function showErr(){
console.log("show");
const el = document.getElementById("error");
el.style.display = "block";
el.innerHTML = "Hey sorry wrong input";
}
window.onload = function() {
hideErr();
}
You can see the HTML and try the code here: https://jsfiddle.net/0mrx5ev7/
You can pass a parameter to your displayErr function, then use it to set the hidden HTML attribute and textContent of a single target div, identified by its HTML id.
This way, the functionality becomes reusable, and you can set/unset the error message whenever you need.
const input = document.querySelector('#input')
const errDisplay = document.querySelector('#err-display')
function displayErr(msg) {
errDisplay.textContent = msg || ''
errDisplay.hidden = msg ? null : 'hidden'
}
input.addEventListener('input', () => {
displayErr(isNaN(input.value) ? "Not a number" : null)
})
#err-display {
font-family: sans-serif;
background: red;
color: white;
margin: .5em 0;
padding: .5em;
}
<input id='input' placeholder='Start typing'>
<div id='err-display' hidden></div>
try to use a counter. like if int i == 0 --> do the function. i would do so
int i = 0;
function displayErr() {
const formBox = document.querySelector("form");
const errorBox = document.createElement("div");
errorBox.className = "errorBox";
const errorText = document.createTextNode("Those are not numbers!");
errorBox.appendChild(errorText);
formBox.appendChild(errorBox);
}
if ((isNaN(billInput)) && i == 0 || (isNaN(peopleAmount)) && i == 0 ||
(billInput === "") && i == 0 || (peopleAmount === "") && i == 0)
{
displayErr();
i += 1;
}
now it will display an error only once, because i is never going to be 0 anymore
I am working on a simple todo list with vanilla JavaScript.
I have succeeded in storing the user's task in local storage and displaying the task on the frontend.
There is a clear task button to remove both tasks from local storage and on the frontend.
It works but not perfectly.
It fails when I do the following:
Add a task
Clear a task
Add a new task and that particular task only appears on the front-end, but on local storage the previous cleared tasks and the new task appears.
If I then reload the browser, the previous task that was cleared both in the frontend and local storage appears both on frontend and local storage.
Please how do I make it work perfectly?
i.e once I clear the task in the local storage, the task does not appear again.
Here is my code below
JavaScript Code snippet
let task = document.querySelector('input');
const form = document.querySelector('form');
const ul = document.querySelector('ul');
const clearTask = document.querySelector('#clear');
// A list for task in local storage
const itemsLocal = localStorage.getItem("items") ? JSON.parse(localStorage.getItem("items")) : [];
localStorage.setItem("items", JSON.stringify(itemsLocal))
// convert local storage data to something I can work with in userData variable
const userData = JSON.parse(localStorage.getItem("items"));
// Function to add task
const addTask = (text) => {
// Create li element
const li = document.createElement('li');
// Create text node
li.appendChild(document.createTextNode(text));
ul.appendChild(li);
}
form.addEventListener('submit',(e) => {
e.preventDefault();
// Add user task to local storage
itemsLocal.push(task.value);
localStorage.setItem("items", JSON.stringify(itemsLocal))
addTask(task.value);
// Clear input field
task.value = '';
})
userData.forEach((data)=> {
addTask(data);
});
clearTask.addEventListener('click', () =>{
localStorage.removeItem("items");
userData.length = 0;
while (ul.firstChild) {
ul.removeChild(ul.firstChild);
}
})
HTML
<body>
<form action="">
<input type="text">
<button type="submit">Add Task</button>
</form>
<div>
<ul>
</ul>
</div>
<button id="clear">Clear Task</button>
<script src="main.js"></script>
</body>
P.s I am new a bit new to JavaScript.
In your clearTask event listener you have to also clear your itemsLocal array.
clearTask.addEventListener('click', () =>{
localStorage.removeItem("items");
itemsLocal.length = 0; // clear it here... (you got to add this line)
userData.length = 0;
while (ul.firstChild) {
ul.removeChild(ul.firstChild);
}
});
Currently, since you're not clearing it, you're adding the new value to the array which still contains the old values and are storing it to local storage.
form.addEventListener('submit',(e) => {
e.preventDefault();
// 'itemsLocal' still has your old values here and you're just appending to it
itemsLocal.push(task.value);
// storing old items and the new one in the local storage here
localStorage.setItem("items", JSON.stringify(itemsLocal));
// you're rendering only the currently added item so you get the
// fake feeling that your list is fine and that local storage has issues
addTask(task.value);
// Clear input field
task.value = '';
});
just make the user data array empty in the clearTask like this:
clearTask.addEventListener('click', () =>{
localStorage.removeItem("items");
itemsLocal = [];
userData = [];
})
elementsList.innerHTML = '' will clear all items in the list.
Below code works perfectly with localStorage and it has one more additional function that can remove individual item.
Html:
<section class="container">
<h1>TO DO LIST</h1>
<ul></ul>
<div class="footer">
<input type="text" placeholder="Title..." />
<button class="enter">Enter</button>
</div>
<div>
<button class="clear">
Clear
</button>
</div>
</section>
Script:
const ul = document.querySelector("ul");
const input = document.querySelector("input");
const enterBtn = document.querySelector(".enter");
const clearBtn = document.querySelector(".clear");
const LIST_LS = "lists";
let lists = [];
function saveStorage() {
localStorage.setItem(LIST_LS, JSON.stringify(lists));
}
function clearStorage() {
lists = [];
ul.innerHTML = "";
saveStorage();
}
function loadStorage() {
const loadStorage = localStorage.getItem(LIST_LS);
if (!loadStorage) {
return;
}
const parsedList = JSON.parse(loadStorage);
parsedList.forEach(list => createItem(list.text));
}
function onAdd() {
const text = input.value;
if (!text) {
return input.focus();
}
createItem(text);
input.value = "";
input.focus();
}
function createItem(text) {
const id = lists.length + 1;
const itemRow = document.createElement("li");
itemRow.setAttribute("class", "item__row");
itemRow.innerHTML = `${text} <i class="fas fa-trash-alt" data-id=${
itemRow.id
}></i>`;
itemRow.id = id;
const delBtn = itemRow.querySelector(".fa-trash-alt");
delBtn.addEventListener("click", deleteItem);
ul.appendChild(itemRow);
lists.push({ text, id });
saveStorage();
return itemRow;
}
function deleteItem(event) {
const trashBtn = event.target;
const li = trashBtn.parentNode;
ul.removeChild(li);
const cleanStorage = lists.filter(toDo => toDo.id !== +li.id);
lists = cleanStorage;
saveStorage();
}
function init() {
loadStorage();
enterBtn.addEventListener("click", () => onAdd());
input.addEventListener("keypress", event => {
if (event.key === "Enter") {
onAdd();
}
});
clearBtn.addEventListener("click", () => clearStorage());
}
init();
https://codesandbox.io/s/damp-morning-csiny
Check this out, I think you might learn from the code:
//<![CDATA[
/* js/external.js */
let get, post, doc, htm, bod, nav, M, I, mobile, S, Q, hC, aC, rC, tC, shuffle, rand, Lister; // for use on other loads
addEventListener('load', ()=>{
get = (url, success, context)=>{
const x = new XMLHttpRequest;
const c = context || x;
x.open('GET', url);
x.onload = ()=>{
if(success)success.call(c, JSON.parse(x.responseText));
}
x.send();
}
post = function(url, send, success, context){
const x = new XMLHttpRequest;
const c = context || x;
x.open('POST', url);
x.onload = ()=>{
if(success)success.call(c, JSON.parse(x.responseText));
}
if(typeof send === 'object' && send && !(send instanceof Array)){
if(send instanceof FormData){
x.send(send);
}
else{
const fd = new FormData;
for(let k in send){
fd.append(k, JSON.stringify(send[k]));
}
x.send(fd);
}
}
else{
throw new Error('send argument must be an Object');
}
return x;
}
doc = document; htm = doc.documentElement; bod = doc.body; nav = navigator; M = tag=>doc.createElement(tag); I = id=>doc.getElementById(id);
mobile = nav.userAgent.match(/Mobi/i) ? true : false;
S = (selector, within)=>{
var w = within || doc;
return w.querySelector(selector);
}
Q = (selector, within)=>{
var w = within || doc;
return w.querySelectorAll(selector);
}
hC = function(node, className){
return node.classList.contains(className);
}
aC = function(){
const a = [].slice.call(arguments), n = a.shift();
n.classList.add(...a);
return aC;
}
rC = function(){
const a = [].slice.call(arguments), n = a.shift();
n.classList.remove(...a);
return rC;
}
tC = function(){
const a = [].slice.call(arguments), n = a.shift();
n.classList.toggle(...a);
return tC;
}
shuffle = array=>{
let a = array.slice(), i = a.length, n, h;
while(i){
n = Math.floor(Math.random()*i--); h = a[i]; a[i] = a[n]; a[n] = h;
}
return a;
}
rand = (min, max)=>{
let mn = min, mx = max;
if(mx === undefined){
mx = mn; mn = 0;
}
return mn+Math.floor(Math.random()*(mx-mn+1));
}
Lister = function(inInput, addButton, outList, clearButton, reverseButton, controlDiv = null){
const o = localStorage.listObj ? JSON.parse(localStorage.listObj) : {lastFirst:true, list:[]}, la = o.list;
outList.innerHTML = '';
this.lastFirst = o.lastFirst;
this.save = ()=>{
localStorage.listObj = JSON.stringify(o);
return this;
}
this.createItem = value=>{
let li = M('li'), x = M('input');
x.className = 'warn'; x.type = 'button'; x.value = 'REMOVE'; li.textContent = value; li.appendChild(x);
x.onclick = ()=>{
for(let i=0,c=outList.children,l=c.length; i<l; i++){
if(c[i] === li){
la.splice(i, 1); break;
}
}
outList.removeChild(li);
if(controlDiv && !outList.hasChildNodes())aC(controlDiv, 'hid');
this.save();
}
return li;
}
this.addItem = value=>{
let v = value.trim();
if(v !== ''){
let li = this.createItem(v), fc = outList.firstChild;
if(this.lastFirst && fc){
outList.insertBefore(li, fc); la.unshift(v);
}
else{
outList.appendChild(li); la.push(v);
}
this.save();
if(controlDiv)rC(controlDiv, 'hid');
}
}
const addIt = ()=>{
this.addItem(inInput.value); inInput.value = ''; rC(inInput, 'good');
}
addButton.onclick = ()=>{
addIt();
}
inInput.onkeydown = e=>{
if(e.key === 'Enter')addIt();
}
inInput.oninput = function(){
const f = this.value.trim() === '' ? rC : aC;
f(this, 'good');
}
clearButton.onclick = function(){
localStorage.removeItem('listObj'); la.splice(0); outList.innerHTML = '';
if(controlDiv)aC(controlDiv, 'hid');
}
this.reverse = ()=>{
la.reverse(); outList.innerHTML = '';
la.forEach(v=>{
outList.appendChild(this.createItem(v));
});
o.lastFirst = this.lastFirst = !this.lastFirst; localStorage.listObj = JSON.stringify(o);
}
reverseButton.onclick = ()=>{
this.reverse();
}
if(la.length){
la.forEach(v=>{
outList.appendChild(this.createItem(v));
});
}
if(controlDiv && outList.hasChildNodes())rC(controlDiv, 'hid');
}
// magic under here
const lister = new Lister(I('input_item'), I('add_item'), I('items'), I('clear'), I('reverse'), I('control'));
}); // end load
//]]>
/* css/external.css */
*{
box-sizing:border-box; font:22px Tahoma, Geneva, sans-serif; color:#000; padding:0; margin:0; overflow:hidden;
}
html,body,.main{
width:100%; height:100%;
}
.main{
background:#333; overflow-y:auto;
}
input[type=text].good{
border:1px solid #0c0;
}
.hid{
display:none;
}
#lister{
padding:10px; background:#aaa;
}
input{
height:38px; padding:3px 5px;
}
input[type=text]{
width:calc(100% - 70px); background:#fff; border:1px solid #c00; border-radius:3px;
}
input[type=button]{
color:#fff; font-weight:bold; border:0; border-radius:5px; cursor:pointer;
}
#add_item{
width:70px; background:linear-gradient(#679467,#235023);
}
li{
position:relative; height:32px; background:#ccc; padding:3px 10px; margin:5px;
}
.warn{
background:linear-gradient(#b75757,#502323); padding:3px 15px;
}
li>.warn{
position:absolute; right:5px; height:26px; font-size:14px;
}
#control{
background:#bbb; text-align:center; padding:5px; border:5px solid #ccc;
}
#reverse.warn{
background:linear-gradient(#1b7bbb,#147);
}
.warn+.warn{
margin-left:20px;
}
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
<head>
<meta charset='UTF-8' /><meta name='viewport' content='width=device-width, height=device-height, initial-scale:1, user-scalable=no' />
<title>Title Here</title>
<link type='text/css' rel='stylesheet' href='css/external.css' />
<script src='js/external.js'></script>
</head>
<body>
<div class='main'>
<div id='lister'>
<input id='input_item' type='text' maxlength='239' /><input id='add_item' type='button' value='ADD' />
<ul id='items'></ul>
<div class='hid' id='control'><input class='warn' id='clear' type='button' value='CLEAR' /><input class='warn' id='reverse' type='button' value='REVERSE' /></div>
</div>
</div>
</body>
</html>
Of course, localStorage will have to be tested on your Server (maybe localhost), since it's an issue on Stack Overflow. They should add virtual localStorage and sessionStorage to this site.
I am having issues getting a button to work. I want something to happen when I click this button:
<button id="pigBtn" value="click">Pig It!</button>
and my JS file has
window.addEventListener('load', function(){
console.log('hello');
let pigBtn = document.querySelector('#pigBtn');
console.log('pigged');
pigBtn.addEventListener('click', function (){
function pigIt(phrase) {
let array = phrase.split(' ');
console.log('array');
for (let i = 0; i < phrase.length; i++) {
let pig = array[i].split('');
let one = pig.shift();
pig.push(one);
pig.push('ay');
let two = pig.join('');
array[i] = two;
}
return array.join(' ');
}
});
});
'hello' and 'pigged' show up but 'array' does not. What am I missing here?
The button has id pigBtn, but you try to select an element with id pigged.
Try this instead:
let pigBtn = document.querySelector('#pigBtn');
window.addEventListener('load', function(){
console.log('hello');
let pigBtn = document.querySelector('#pigBtn');
console.log('pigged');
pigBtn.addEventListener('click', function (){
console.log('clicked');
});
});
<button id="pigBtn" value="click">Pig It!</button>
window.addEventListener('load', function(){
console.log('hello');
let pigBtn = document.querySelector('#pigBtn');
console.log('pigged');
pigBtn.addEventListener('click', function (){
console.log('clicked');
});
});
The ID in your querySelector has to match the ID of the button in your HTML.
https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
Your pigIt function is never executed. You define it in your event handler, but you never run it.
I think you're going for something like this :
window.addEventListener('load', function(){
var sentence = "This is the phrase I'm pigging";
console.log('hello');
let pigBtn = document.getElementById('pigBtn');
let pigTxt = document.getElementById('phraseTxt');
let pigPhraseTxt = document.getElementById('pigPhraseTxt');
console.log('pigged');
pigBtn.addEventListener('click', function(e) {
let array = pigTxt.value.split(' ');
for (let i = 0; i < array.length; i++) {
let pig = array[i].split('');
let one = pig.shift();
pig.push(one);
pig.push('ay');
let two = pig.join('');
array[i] = two;
}
pigPhraseTxt.value = array.join(' ');
});
});
input {
display : block;
width : 100%;
padding: 0;
border: 1px solid #aaa;
}
input:read-only {
background: #ddd;
}
<input type="text" id="phraseTxt" value="This is the text I'm converting to Pig Latin" />
<input type="text" id="pigPhraseTxt" readonly />
<button id="pigBtn" value="click">Pig It!</button>
So I have a code that runs the css and html fine in phonegap, but the javascript items do not function. For example I'm making a To Do app, but the button will not save my new item, or click to delete.
css code
body
{
font-family: Verdana, Arial;
font-size: 18px;
background-color:#D4D0B4;
}
h1
{
background-color:#626b5e;
font-size:1em;
color:#F5F6F5;
line-height:2em;
text-align:center;
}
#newTaskInput, #addNewTask
{
display:block;
width:98%;
margin-top:5px;
margin-left:auto;
margin-right:auto;
background-color:#757769;
border:0;
height;2em;
font-size:1em;
color:#F5F6F5;
}
#taskList
{
margin-top:10px;
}
#taskList > li
{
background: -webkit-linear-gradient(#FFF, #F6F6F7);
background: -o-linear-gradient(#FFF, #F6F6F7);
background: -moz-linear-gradient(#FFF, #F6F6F7);
background: linear-gradient(#FFF, #F6F6F7);
border:1px solid #BBB6AF
line-height:2em;
color:#929292;
margin-top:2px;
}
#taskList span
{
margin-left:5px;
}
.done
{
text-decoration:line-through;
opacity:0.5;
}
HTML
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="type/css" href="css/reset.css">
<link rel="stylesheet" type="text/css" href="css/index.css" />
<title>Todo List</title>
</head>
<body>
<h1>Todo List</h1>
<div id="newTaskSection">
<input type="text" id="newTaskInput" placeholder="New Task">
<button id="addNewTask">Add</button>
</div>
<ul id="taskList">
</ul>
<script type="text/javascript" src="js/jquery-2.1.4.min.js"></script>
<script type="text/javascript" src="js/index.js"></script>
</body>
</html>
javascript
var taskList = new Array();
$( document ).ready(function(){
var $newTaskInput = $('#newTaskInput');
var $taskList = $('#taskList');
var taskTouchStart;
var taskTouchEnd;
var taskTouchStartX;
var taskTouchEndX;
if( window.localStorage )
{
taskList = JSON.parse(window.localStorage.getItem('taskList'));
}
if(null !== taskList)
{
for(i=0;i<taskList.length;i++)
{
var newTask = '<li data-key="' + taskList[1].key + '"><span>' + taskList[i].task + '</span></li>';
$taskList.append(newTask);
}
}
else
{
taskList = new Array();
}
$('#addNewTask').on('click', function(){
var key = Date.now();
var newTask = '<li data-key="' + key + '"><span>' + $newTaskInput.val() + '</span></li>';
$taskList.append( newTask );
taskList.push({key:key, task:$newTaskInput.val(), done:false});
if(window.localStorage)
{
window.localStorage.setItem('taskList', JSON.stringify(taskList));
}
$newTaskInput.val('');
});
$taskList.on('touchstart', 'li', function(e){
var start = document.elementFromPoint( e.originalEvent.touches[0].pageX, e.originalEvent.touches[0].pageY);
taskTouchStart = $(start).attr('data-key');
taskTouchStartX = e.originalEvent.touches[0].pageX;
});
$taskList.on('touchend', 'li', function(e){
var $end;
var $this = $(this);
var end = document.elementFromPoint( e.originalEvent.touches[0].pageX, e.originalEvent.touches[0].pageY);
$end = $(end);
taskTouchEnd = $end.attr('data-key');
taskTouchEndX = e.originalEvent.touches[0].pageX;
if(taskTouchStart == taskTouchEnd)
{
if(taskTouchStartX < taskTouchEndX)
{
if($this.hasClass('done'))
{
$this.removeClass('done');
}
else
{
$this.addClass('done');
}
}
else
{
taskList = $.grep(taskList, function(e){ return e.key != taskTouchEnd;});
if(window.localStorage)
{
window.localStorage.setItem('taskList', JSON.stringify(taskList));
}
$end.remove();
}
}
});
});
Simplified form of the done/not done handler in the code below, which would replace your current on('touchstart.. and on('touchend... blocks and remove a lot of complexity:
Additionally, you have a 1 where there should be an i in your display block (unless I mistook the purpose), and you're not setting the done class for tasks from localStorage which are marked such.
Changes commented in the code below, which would replace the JS you posed above.
Also, apologies, but I've mixed jQuery and vanilla JS, and only went so far as to get a working example, you'll have to work further on validation on what not, hopefully this gets you going.
$( document ).ready(function(){
var $newTaskInput = $('#newTaskInput');
var $taskList = $('#taskList');
if( window.localStorage ){
taskList = JSON.parse(window.localStorage.getItem('taskList'));
}
if(null !== taskList){
for(i=0;i<taskList.length;i++){
// Should we add the 'done' class to these items?
var newTaskClass = (taskList[i].done)? 'done': '';
// taskList[1].key to taskList[i].key ??? or am I missing something?
var newTask = '<li data-key="' + taskList[i].key + '" class="' + newTaskClass + '"><span>' + taskList[i].task + '</span></li>';
$taskList.append(newTask);
}
}
else {
taskList = new Array();
}
$('#addNewTask').on('click', function(){
var key = Date.now();
var newTask = '<li data-key="' + key + '"><span>' + $newTaskInput.val() + '</span></li>';
$taskList.append( newTask );
taskList.push({key:key, task:$newTaskInput.val(), done:false});
if(window.localStorage)
{
window.localStorage.setItem('taskList', JSON.stringify(taskList));
}
$newTaskInput.val('');
});
// Replaces the 'touchstart/end' handlers
$(document).on('click', '#taskList li', function(e){
var task = $(this);
// Update the li class
if (task.hasClass('done')){
task.removeClass('done');
} else {
task.addClass('done');
}
// Find the item by its key property (assumes key exists / no duplicates)
var itemToUpdate = taskList.filter(function(item){
return item.key === task.data('key');
})[0];
// If true, make false, if false, make ture
itemToUpdate.done = !itemToUpdate.done;
// Over-write the task list in local storage
window.localStorage.setItem('taskList', JSON.stringify(taskList));
});
});