Autocomplete Search bar won't read my values - javascript

im trying to make a search bar for my website to redirect pepole on other pages of my website by selecting values from autocomplete suggestions, but when i select a suggestion (example:"Home") and hit search nothing happends, instead when i write the value (example:"chat") it works just fine and it redirects me to another page, my question is: what im doing wrong and why my autocompleted values are not seen by the searchbar?
Here is the code example for values "chat, Home, youtube"
function ouvrirPage() {
var a = document.getElementById("search").value;
if (a === "chat") {
window.open("/index.html");
}
if (a === "Home") {
window.open("/customizedalert.html");
}
if (a === "youtube") {
window.open("https://www.youtube.com/");
}
}
And here is the entire thing:
https://codepen.io/galusk0149007/pen/LYeXvww

Try this in your IDE : Clicking the search icon will navigate to your urls.
// getting all required elements
const searchWrapper = document.querySelector(".search-input");
const inputBox = searchWrapper.querySelector("input");
const suggBox = searchWrapper.querySelector(".autocom-box");
const icon = searchWrapper.querySelector(".icon");
let linkTag = searchWrapper.querySelector("a");
let webLink;
let suggestions = ['chat','home', 'youtube']
// if user press any key and release
inputBox.onkeyup = (e)=>{
let userData = e.target.value; //user enetered data
let emptyArray = [];
if(userData){
icon.onclick = ()=>{
webLink = `https://www.google.com/search?q=${userData}`;
linkTag.setAttribute("href", webLink);
linkTag.click();
}
emptyArray = suggestions.filter((data)=>{
//filtering array value and user characters to lowercase and return only those words which are start with user enetered chars
return data.toLocaleLowerCase().startsWith(userData.toLocaleLowerCase());
});
emptyArray = emptyArray.map((data)=>{
// passing return data inside li tag
return data = `<li>${data}</li>`;
});
searchWrapper.classList.add("active"); //show autocomplete box
showSuggestions(emptyArray);
let allList = suggBox.querySelectorAll("li");
for (let i = 0; i < allList.length; i++) {
//adding onclick attribute in all li tag
allList[i].setAttribute("onclick", "select(this)");
}
}else{
searchWrapper.classList.remove("active"); //hide autocomplete box
}
}
function select(element){
let selectData = element.textContent;
inputBox.value = selectData;
icon.onclick = ()=>{
webLink = `https://www.google.com/search?q=${selectData}`;
linkTag.setAttribute("href", webLink);
linkTag.click();
}
searchWrapper.classList.remove("active");
}
function showSuggestions(list){
let listData;
if(!list.length){
userValue = inputBox.value;
listData = `<li>${userValue}</li>`;
}else{
listData = list.join('');
}
suggBox.innerHTML = listData;
}
function ouvrirPage() {
var a = document.getElementById("search").value;
if (a === "chat") {
window.open("/index.html");
}
if (a === "Home") {
window.open("/customizedalert.html");
}
if (a === "youtube") {
window.open("https://www.youtube.com/");
}
}
#import url('https://fonts.googleapis.com/css2?family=Poppins:wght#200;300;400;500;600;700&display=swap');
*{
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body{
background: #544b8b;
padding: 0 20px;
}
::selection{
color: #fff;
background: #7c71bd;
}
.wrapper{
max-width: 450px;
margin: 150px auto;
}
.wrapper .search-input{
background: #fff;
width: 100%;
border-radius: 5px;
position: relative;
box-shadow: 0px 1px 5px 3px rgba(0,0,0,0.12);
}
.search-input input{
height: 55px;
width: 100%;
outline: none;
border: none;
border-radius: 5px;
padding: 0 60px 0 20px;
font-size: 18px;
box-shadow: 0px 1px 5px rgba(0,0,0,0.1);
}
.search-input.active input{
border-radius: 5px 5px 0 0;
}
.search-input .autocom-box{
padding: 0;
opacity: 0;
pointer-events: none;
max-height: 280px;
overflow-y: auto;
}
.search-input.active .autocom-box{
padding: 10px 8px;
opacity: 1;
pointer-events: auto;
}
.autocom-box li{
list-style: none;
padding: 8px 12px;
display: none;
width: 100%;
cursor: default;
border-radius: 3px;
}
.search-input.active .autocom-box li{
display: block;
}
.autocom-box li:hover{
background: #efefef;
}
.search-input .icon{
position: absolute;
right: 0px;
top: 0px;
height: 55px;
width: 55px;
text-align: center;
line-height: 55px;
font-size: 20px;
color: #644bff;
cursor: pointer;
}
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"/>
</head>
<body>
<div class="wrapper">
<div class="search-input">
<a href="" target="_blank" hidden></a>
<input type="text" placeholder="Type to search..">
<div class="autocom-box">
</div>
<div class="icon" onclick="ouvrirPage()"><i class="fas fa-search"></i></div>
</div>
</div>
</body>

Related

How to submit an list when one of the list item is selected

hello i am creating a search bar that has auto complete feature can someone tell how to auto submit when one of the option is selected
some other possible solutions are autosubmit when the input field contains $
{i will add a $ swmbol in each of the array}
you can check the whole program on codepen by clicking here
https://codepen.io/simplyrajatgupta/pen/wvmXPJv
here is the codes
let suggestions = [
"Channel",
"CodingLab",
"CodingNepal",
"YouTube",
"YouTuber",
"YouTube Channel",
"Blogger",
"Bollywood",
"Vlogger",
"Vechiles",
"Facebook",
"Freelancer",
"Facebook Page",
"Designer",
"Developer",
"Web Designer",
"Web Developer",
"Login Form in HTML & CSS",
"How to learn HTML & CSS",
"How to learn JavaScript",
"How to become Freelancer",
"How to become Web Designer",
"How to start Gaming Channel",
"How to start YouTube Channel",
"What does HTML stands for?",
"What does CSS stands for?",
];
// getting all required elements
const searchWrapper = document.querySelector(".search-input");
const inputBox = searchWrapper.querySelector("input");
const suggBox = searchWrapper.querySelector(".autocom-box");
const icon = searchWrapper.querySelector(".icon");
let linkTag = searchWrapper.querySelector("a");
let webLink;
// if user press any key and release
inputBox.onkeyup = (e)=>{
let userData = e.target.value; //user enetered data
let emptyArray = [];
if(userData){
icon.onclick = ()=>{
webLink = `https://www.google.com/search?q=${userData}`;
linkTag.setAttribute("href", webLink);
linkTag.click();
}
emptyArray = suggestions.filter((data)=>{
//filtering array value and user characters to lowercase and return only those words which are start with user enetered chars
return data.toLocaleLowerCase().startsWith(userData.toLocaleLowerCase());
});
emptyArray = emptyArray.map((data)=>{
// passing return data inside li tag
return data = `<li>${data}</li>`;
});
searchWrapper.classList.add("active"); //show autocomplete box
showSuggestions(emptyArray);
let allList = suggBox.querySelectorAll("li");
for (let i = 0; i < allList.length; i++) {
//adding onclick attribute in all li tag
allList[i].setAttribute("onclick", "select(this)");
}
}else{
searchWrapper.classList.remove("active"); //hide autocomplete box
}
}
function select(element){
let selectData = element.textContent;
inputBox.value = selectData;
icon.onclick = ()=>{
webLink = `https://www.google.com/search?q=${selectData}`;
linkTag.setAttribute("href", webLink);
linkTag.click();
}
searchWrapper.classList.remove("active");
}
function showSuggestions(list){
let listData;
if(!list.length){
userValue = inputBox.value;
listData = `<li>${userValue}</li>`;
}else{
listData = list.join('');
}
suggBox.innerHTML = listData;
}
#import url('https://fonts.googleapis.com/css2?family=Poppins:wght#200;300;400;500;600;700&display=swap');
*{
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body{
background: #644bff;
padding: 0 20px;
}
::selection{
color: #fff;
background: #664AFF;
}
.wrapper{
max-width: 450px;
margin: 150px auto;
}
.wrapper .search-input{
background: #fff;
width: 100%;
border-radius: 5px;
position: relative;
box-shadow: 0px 1px 5px 3px rgba(0,0,0,0.12);
}
.search-input input{
height: 55px;
width: 100%;
outline: none;
border: none;
border-radius: 5px;
padding: 0 60px 0 20px;
font-size: 18px;
box-shadow: 0px 1px 5px rgba(0,0,0,0.1);
}
.search-input.active input{
border-radius: 5px 5px 0 0;
}
.search-input .autocom-box{
padding: 0;
opacity: 0;
pointer-events: none;
max-height: 280px;
overflow-y: auto;
}
.search-input.active .autocom-box{
padding: 10px 8px;
opacity: 1;
pointer-events: auto;
}
.autocom-box li{
list-style: none;
padding: 8px 12px;
display: none;
width: 100%;
cursor: default;
border-radius: 3px;
}
.search-input.active .autocom-box li{
display: block;
}
.autocom-box li:hover{
background: #1bb361;
color: white;
}
.search-input .icon{
position: absolute;
right: 0px;
top: 0px;
height: 45px;
;
width: 45px;
text-align: center;
line-height: 45px;
font-size: 20px;
color: white;
cursor: pointer;
background: #1a74f2;
right: 5px;
top: 5px;
bottom: 5px;
border-radius: 5px;
}
.search-input
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Autocomplete Search Box | CodingNepal</title>
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"/>
</head>
<body>
<form action="https://www.google.com/search?q=">
<div class="wrapper">
<div class="search-input">
<a href="" target="_blank" hidden></a>
<input type="text" id="ra"placeholder="Enter question or chapter...">
<div class="autocom-box">
<!-- here list are inserted from javascript -->
</div>
<div class="icon" id="ca"type="submit"><i class="fas fa-search"></i></div>
</div>
</div>
</form>
<script src="js/suggestions.js"></script>
<script src="js/script.js"></script>
<script>
var input = document.getElementById("ra");
input.addEventListener("keypress", function(event) {
if (event.key === "Enter") {
event.preventDefault();
document.getElementsByClassName("ca").click();
}
});
</script>
</body>
</html>
Instead write code for clicking search button, you can just call window.open('http://url-goes-here.com').
inputBox.onkeydown = (e) => {
let firstSuggestion = suggestions.filter(el =>
el.toLowerCase().includes(e.target.value.toLowerCase()))[0];
if((e.keyCode == 13) && firstSuggestion)
e.preventDefault();
window.open(`https://www.google.com/search?q=${firstSuggestion}`)
}
}
This function make input that matched with first suggestion auto-submitted when enter key pressed. If you wanna use this function, make sure you delete another function that also record enter key in input.
.onkeydown make any key pressed in inputBox will be 'record'. If the key pressed is enter (enter keycode = 13) and first suggestion exist (not null), so you will be redirect to google url + first suggestion query.
firstSuggestion in here, defined as filtered suggestion list, which only include suggestion that contains character of your search query.

How to add a edit to-do list item feature?

Ive been studying javascript and following along on this online tutorial for a to-do list. I switched up and added a few of my own features, but I am not sure how would go about adding a feature where I can edit a individual list item?
I started off creating a function editTodo(key), I know I would have to append my new text to the old list text? If someone could give me a hint or guide me in the right direction?
//array that holds todo list items
let listItems = [];
//Function will create a new list item based on whatever the input value
//was entered in the text input
function addItem (text) {
const todo = {
text, //whatever user types in
checked: false, //boolean which lets us know if a task been marked complete
id: Date.now(), //unique identifier for item
};
//it is then pushed onto the listItems array
listItems.push(todo);
renderTodo(todo);
}
function checkDone(key) {
//findIndex is an array method that returns position of an element in array
const index = listItems.findIndex(item => item.id === Number(key));
//locates the todo item in the listItems array and set its checked property
//to opposite. 'true' will become 'false'
listItems[index].checked = !listItems[index].checked;
renderTodo(listItems[index]);
}
function deleteTodo(key) {
//find todo object in the listItems array
const index = listItems.findIndex(item => item.id === Number(key));
//create a new object with properties of the current list item
//delete property set to true
const todo = {
deleted: true,
...listItems[index]
};
//remove the list item from the array by filtering it out
listItems = listItems.filter(item => item.id !== Number(key));
renderTodo(todo);
}
//edits list item
function editTodo(key) {
//find todo object in the listItems array
const index = listItems.findIndex(item => item.id === Number(key));
}
//selects form element
const form = document.querySelector('.js-form');
const addGoal = document.getElementById('addBtn');
//adds a submit event listener
function selectForm(event) {
//prevent page refresh on form submission
event.preventDefault();
//select the text input
const input = document.querySelector('.js-todo-input');
//gets value of the input and removes whitespace beginning/end of string
//we then save that to new variable -> text
const text = input.value.trim();
//checks whether 2 operands are not equal, returning true or false (boolean)
//if input value is not equal to blank, add user input
if (text !== '') {
addItem(text);
input.value = ''; //value of text input is cleared by setting it to empty
input.focus(); //focused so user can add many items to list witout focusing the input
}
};
addGoal.addEventListener('click', selectForm, false);
form.addEventListener('submit', selectForm, false);
function renderTodo(todo) {
//saves local storage items, convert listItems array to JSON string
localStorage.setItem('listItemsRef', JSON.stringify(listItems));
//selects the first element with a class of 'js-to'list'
const list = document.querySelector('.js-todo-list');
//selects current todo (refer to top) list item in DOM
const item = document.querySelector(`[data-key='${todo.id}']`);
//refer to function deleteTodo(key)
if (todo.deleted) {
//remove item from DOM
item.remove();
return
}
//use the ternary operator to check if 'todo.checked' is true
//if true, assigns 'done' to checkMarked. if not, assigns empty string
const checkMarked = todo.checked ? 'done' : '';
//creates list 'li' item and assigns it to 'goal'
const goal = document.createElement('li');
//sets the class attribute
goal.setAttribute('class', `todo-item ${checkMarked}`);
//sets the data-key attribute to the id of the todo
goal.setAttribute('data-key', todo.id);
//sets the contents of the list item 'li'
goal.innerHTML = `
<input id="${todo.id}" type="checkbox" />
<label for="${todo.id}" class="tick js-tick"></label>
<span>${todo.text}</span>
<button class="edit-todo js-edit-todo"><i class="fa-solid fa-pencil"></i></button>
<button class="delete-todo js-delete-todo">X</button>
`;
//if item already exists in the DOM
if (item) {
//replace it
list.replaceChild(goal, item);
}else {
//otherwise if it doesnt (new list items) add at the end of the list
list.append(goal);
}
}
//selects entire list
const list = document.querySelector('.js-todo-list');
//adds click event listener to the list and its children
list.addEventListener('click', event => {
if (event.target.classList.contains('js-tick')) {
const itemKey = event.target.parentElement.dataset.key;
checkDone(itemKey);
}
//add this 'if block
if (event.target.classList.contains('js-delete-todo')) {
const itemKey = event.target.parentElement.dataset.key;
deleteTodo(itemKey);
}
})
//render any existing listItem when page is loaded
document.addEventListener('DOMContentLoaded', () => {
const ref = localStorage.getItem('listItemsRef');
if (ref) {
listItems = JSON.parse(ref);
listItems.forEach(t => {
renderTodo(t);
});
}
});
#import url('https://fonts.googleapis.com/css2?family=Montserrat&display=swap');
html {
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
margin: 0;
padding: 0;
}
body {
font-family: 'Montserrat', sans-serif;
line-height: 1.4;
}
.container {
width: 100%;
max-width: 500px;
margin: 0 auto;
padding-left: 10px;
padding-right: 10px;
color: rgb(43, 43, 43);
height: 90vh;
margin-top: 20vh;
margin-bottom: 5vh;
overflow-y: auto;
}
.app-title {
text-align: center;
margin-bottom: 20px;
font-size: 80px;
opacity: 0.5;
}
.todo-list {
list-style: none;
margin-top: 20px;
}
.todo-item {
margin-bottom: 10px;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.todo-item span {
flex-grow: 1;
margin-left: 10px;
margin-right: 10px;
font-size: 22px;
}
.done span {
background-color:#0099e5;
color:#fff;
}
input[type="checkbox"] {
display: none;
}
#addBtn {
padding: 8px 16px;
font-size:16px;
font-weight:bold;
text-decoration: none;
background-color:#0099e5;
color:#fff;
border-radius: 3px;
border: 3px solid #333;
margin-left:10px;
cursor:pointer;
}
#addBtn:hover {
background-color:#0078b4;
}
.tick {
width: 30px;
height: 30px;
border: 3px solid #333;
border-radius: 50%;
display: inline-flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.tick::before {
content: '✓';
font-size: 20px;
display: none;
}
.done .tick::before {
display: inline;
}
.delete-todo {
border: none;
font-size:16px;
background-color:red;
color:#fff;
outline: none;
cursor: pointer;
width: 28px;
height: 28px;
border-radius:20px;
}
.edit-todo {
border: none;
font-size:16px;
background-color:green;
color:#fff;
outline: none;
cursor: pointer;
width: 28px;
height: 28px;
border-radius:20px;
}
.empty-warning {
flex-direction:column;
align-items:center;
justify-content:center;
display:none;
}
.todo-list:empty {
display:none;
}
.todo-list:empty + .empty-warning {
display:flex;
}
.empty-warning-title {
margin-top:15px;
opacity: 0.8;
color: rgb(43, 43, 43);
}
form {
width: 100%;
display: flex;
justify-content: space-between;
margin-left:5px;
}
input[type="text"] {
width: 100%;
padding: 10px;
border-radius: 4px;
border: 3px solid #333;
}
<!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">
<title>To-Do List</title>
<link rel = "stylesheet" href = "style.css">
<script src="https://kit.fontawesome.com/67e5409c20.js" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<h1 class="app-title">To Do List</h1>
<form class="js-form">
<input autofocus type="text" aria-label="Enter a new todo item" placeholder="Ex - Walk the dog" class="js-todo-input">
<input type="button" id="addBtn" value="Add">
</form>
<ul class="todo-list js-todo-list"></ul>
<div class="empty-warning">
<h2 class="empty-warning-title">Add your first goal</h2>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
I added the edit feature to the event handler for click events on the list:
Figure I
if (event.target.matches('.edit-todo') && event.target !== event.currentTarget) {
const text = event.target.previousElementSibling;
text.toggleAttribute('contenteditable');
if (text.contenteditable) {
text.focus();
}
}
Basically, when the user clicks an edit button the contenteditable attribute is toggled (true/false) on the <span> that sits right before the button (hence .previousElementSibling).
I also added 2 CSS rulesets as well:
Figure II
.fa-pencil { pointer-events: none }
[contenteditable] { outline: 3px inset blue }
For some reason my mouse cannot click font-awesome icons, I have no idea why. So I disabled click events on the edit icon in order to click the edit button. Others might have the same problem as I do -- I'm 99% sure there's no harm in keeping that ruleset since it just makes the edit button 100% the origin element on the event chain. The second ruleset is a visual cue to the user that the <span> is editable.
let listItems = [];
function addItem(text) {
const todo = {
text,
checked: false,
id: Date.now(),
};
listItems.push(todo);
renderTodo(todo);
}
function checkDone(key) {
const index = listItems.findIndex(item => item.id === Number(key));
listItems[index].checked = !listItems[index].checked;
renderTodo(listItems[index]);
}
function deleteTodo(key) {
const index = listItems.findIndex(item => item.id === Number(key));
const todo = {
deleted: true,
...listItems[index]
};
listItems = listItems.filter(item => item.id !== Number(key));
renderTodo(todo);
}
function editTodo(key) {
const index = listItems.findIndex(item => item.id === Number(key));
}
const form = document.querySelector('.js-form');
const addGoal = document.getElementById('addBtn');
function selectForm(event) {
event.preventDefault();
const input = document.querySelector('.js-todo-input');
const text = input.value.trim();
if (text !== '') {
addItem(text);
input.value = '';
input.focus();
}
};
addGoal.addEventListener('click', selectForm, false);
form.addEventListener('submit', selectForm, false);
function renderTodo(todo) {
// localStorage.setItem('listItemsRef', JSON.stringify(listItems));
const list = document.querySelector('.js-todo-list');
const item = document.querySelector(`[data-key='${todo.id}']`);
if (todo.deleted) {
item.remove();
return
}
const checkMarked = todo.checked ? 'done' : '';
const goal = document.createElement('li');
goal.setAttribute('class', `todo-item ${checkMarked}`);
goal.setAttribute('data-key', todo.id);
goal.innerHTML = `
<input id="${todo.id}" type="checkbox" />
<label for="${todo.id}" class="tick js-tick"></label>
<span>${todo.text}</span>
<button class="edit-todo js-edit-todo"><i class="fa-solid fa-pencil"></i></button>
<button class="delete-todo js-delete-todo">X</button>
`;
if (item) {
list.replaceChild(goal, item);
} else {
list.append(goal);
}
}
const list = document.querySelector('.js-todo-list');
list.addEventListener('click', function(event) {
if (event.target.classList.contains('js-tick')) {
const itemKey = event.target.parentElement.dataset.key;
checkDone(itemKey);
}
if (event.target.classList.contains('js-delete-todo')) {
const itemKey = event.target.parentElement.dataset.key;
deleteTodo(itemKey);
}
if (event.target.matches('.edit-todo') && event.target !== event.currentTarget) {
const text = event.target.previousElementSibling;
text.toggleAttribute('contenteditable');
if (text.contenteditable) {
text.focus();
}
}
})
/*
document.addEventListener('DOMContentLoaded', () => {
const ref = localStorage.getItem('listItemsRef');
if (ref) {
listItems = JSON.parse(ref);
listItems.forEach(t => {
renderTodo(t);
});
}
});*/
#import url('https://fonts.googleapis.com/css2?family=Montserrat&display=swap');
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
margin: 0;
padding: 0;
}
body {
font-family: 'Montserrat', sans-serif;
line-height: 1.4;
}
.container {
width: 100%;
max-width: 500px;
margin: 0 auto;
padding-left: 10px;
padding-right: 10px;
color: rgb(43, 43, 43);
height: 90vh;
margin-top: 20vh;
margin-bottom: 5vh;
overflow-y: auto;
}
.app-title {
text-align: center;
margin-bottom: 20px;
font-size: 80px;
opacity: 0.5;
}
.todo-list {
list-style: none;
margin-top: 20px;
}
.todo-item {
margin-bottom: 10px;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.todo-item span {
flex-grow: 1;
margin-left: 10px;
margin-right: 10px;
font-size: 22px;
}
.done span {
background-color: #0099e5;
color: #fff;
}
input[type="checkbox"] {
display: none;
}
#addBtn {
padding: 8px 16px;
font-size: 16px;
font-weight: bold;
text-decoration: none;
background-color: #0099e5;
color: #fff;
border-radius: 3px;
border: 3px solid #333;
margin-left: 10px;
cursor: pointer;
}
#addBtn:hover {
background-color: #0078b4;
}
.tick {
width: 30px;
height: 30px;
border: 3px solid #333;
border-radius: 50%;
display: inline-flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.tick::before {
content: '✓';
font-size: 20px;
display: none;
}
.done .tick::before {
display: inline;
}
.delete-todo {
border: none;
font-size: 16px;
background-color: red;
color: #fff;
outline: none;
cursor: pointer;
width: 28px;
height: 28px;
border-radius: 20px;
}
.edit-todo {
border: none;
font-size: 16px;
background-color: green;
color: #fff;
outline: none;
cursor: pointer;
width: 28px;
height: 28px;
border-radius: 20px;
}
.empty-warning {
flex-direction: column;
align-items: center;
justify-content: center;
display: none;
}
.todo-list:empty {
display: none;
}
.todo-list:empty+.empty-warning {
display: flex;
}
.empty-warning-title {
margin-top: 15px;
opacity: 0.8;
color: rgb(43, 43, 43);
}
form {
width: 100%;
display: flex;
justify-content: space-between;
margin-left: 5px;
}
input[type="text"] {
width: 100%;
padding: 10px;
border-radius: 4px;
border: 3px solid #333;
}
.fa-pencil {
pointer-events: none
}
[contenteditable] {
outline: 3px inset blue
}
<!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">
<title>To-Do List</title>
<link rel="stylesheet" href="style.css">
<script src="https://kit.fontawesome.com/67e5409c20.js" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<h1 class="app-title">To Do List</h1>
<form class="js-form">
<input autofocus type="text" aria-label="Enter a new todo item" placeholder="Ex - Walk the dog" class="js-todo-input">
<input type="button" id="addBtn" value="Add">
</form>
<ul class="todo-list js-todo-list"></ul>
<div class="empty-warning">
<h2 class="empty-warning-title">Add your first goal</h2>
</div>
</div>
<script src="script.js"></script>
</body>
</html>

Function removes items from localStorage only if run manually

I'm looking for advice/tips on how to fix a function that is supposed to remove items from localStorage. I'm following a tutorial by John Smilga that I found on Youtube. Although I've modeled my code on his, apparently, I have missed something.
This function works perfectly well if I run it manually from the console and pass in the id of the item that I want to remove from localStorage.
function removeFromLocalStorage(id) {
console.log(id);
let storageItems = getLocalStorage();
console.log(storageItems);
storageItems = storageItems.filter(function(singleItem) {
if (singleItem.id !== id) {
return singleItem;
}
})
console.log(storageItems);
localStorage.setItem("list", JSON.stringify(storageItems));
}
However, when this function is triggered by the deleteItem() function, it refuses to remove the item from localStorage. It still works, there are a bunch of console.logs in it that track its execution, and I can check that it receives the correct item id as the argument, but for some reason it doesn't filter out the item that needs to be removed. I am completely lost and have no idea how to identify the problem. I can't debug it with console.logs as I usually do. I will be very grateful if you help me find the problem. Any advice will be appreciated.
In case the entire code is needed, please find it below.
const form = document.querySelector(".app__form");
const alert = document.querySelector(".app__alert");
const input = document.querySelector(".app__input");
const submitBtn = document.querySelector(".app__submit-btn");
const itemsContainer = document.querySelector(".app__items-container");
const itemsList = document.querySelector(".app__items-list");
const clearBtn = document.querySelector(".app__clear-btn");
let editElement;
let editFlag = false;
let editId = "";
form.addEventListener("submit", addItem);
clearBtn.addEventListener("click", clearItems);
function addItem(e) {
e.preventDefault();
const id = Math.floor(Math.random() * 9999999999);
if (input.value && !editFlag) {
const item = document.createElement("div");
item.classList.add("app__item");
const attr = document.createAttribute("data-id");
attr.value = id;
item.setAttributeNode(attr);
item.innerHTML = `<p class='app__item-text'>${input.value}</p>
<div class='app__item-btn-cont'>
<button class='app__item-btn app__item-btn--edit'>edit</button>
<button class='app__item-btn app__item-btn--delete'>delete</button>
</div>`
const editBtn = item.querySelector(".app__item-btn--edit");
const deleteBtn = item.querySelector(".app__item-btn--delete");
editBtn.addEventListener("click", editItem);
deleteBtn.addEventListener("click", deleteItem);
itemsList.appendChild(item);
displayAlert("item added", "success");
addToLocalStorage(id, input.value);
setBackToDefault();
itemsContainer.classList.add("app__items-container--visible");
} else if (input.value && editFlag) {
editElement.textContent = input.value;
// edit local storage
editLocalStorage(editId, input.value);
setBackToDefault();
displayAlert("item edited", "success");
} else {
displayAlert("empty field", "warning");
}
}
function setBackToDefault() {
input.value = "";
editFlag = false;
editId = "";
submitBtn.textContent = "Submit";
submitBtn.className = "app__submit-btn";
}
function displayAlert(text, action) {
alert.textContent = text;
alert.classList.add(`app__alert--${action}`);
setTimeout(function() {
alert.textContent = "";
alert.classList.remove(`app__alert--${action}`);
}, 700)
}
function clearItems() {
const items = document.querySelectorAll(".app__item");
if (items.length > 0) {
items.forEach(function(singleItem) {
itemsList.removeChild(singleItem);
})
itemsContainer.classList.remove("app__items-container--visible");
displayAlert("items cleared", "cleared");
setBackToDefault();
}
}
function editItem(e) {
const item = e.currentTarget.parentElement.parentElement;
editElement = e.currentTarget.parentElement.previousElementSibling;
editId = item.dataset.id;
editFlag = true;
input.value = editElement.textContent;
submitBtn.textContent = "Edit";
submitBtn.classList.add("app__submit-btn--edit");
input.focus();
}
function deleteItem(e) {
const item = e.currentTarget.parentElement.parentElement;
const itemId = item.dataset.id;
removeFromLocalStorage(itemId);
displayAlert("item removed", "cleared");
setBackToDefault();
itemsList.removeChild(item);
if (itemsList.children.length === 0) {
itemsContainer.classList.remove("app__items-container--visible");
}
}
function addToLocalStorage(id, value) {
const itemsObj = {id: id, value: input.value};
let storageItems = getLocalStorage();
storageItems.push(itemsObj);
localStorage.setItem("list", JSON.stringify(storageItems));
}
function removeFromLocalStorage(id) {
console.log(id);
let storageItems = getLocalStorage();
console.log(storageItems);
storageItems = storageItems.filter(function(singleItem) {
if (singleItem.id !== id) {
return singleItem;
}
})
console.log(storageItems);
localStorage.setItem("list", JSON.stringify(storageItems));
}
function editLocalStorage(id, value) {
}
function getLocalStorage() {
return localStorage.getItem("list") ? JSON.parse(localStorage.getItem("list")) : [];
}
* {
margin: 0;
padding: 0;
}
.app {
width: 70%;
max-width: 600px;
margin: 75px auto 0;
}
.app__title {
text-align: center;
/* color: #1B5D81; */
margin-top: 20px;
color: #377FB4;
}
.app__alert {
width: 60%;
margin: 0 auto;
text-align: center;
font-size: 20px;
color: #215884;
border-radius: 7px;
height: 23px;
transition: 0.4s;
text-transform: capitalize;
}
.app__alert--warning {
background-color: rgba(243, 117, 66, 0.2);
color: #006699;
}
.app__alert--success {
background-color: rgba(165, 237, 92, 0.4);
color: #3333ff;
}
.app__alert--cleared {
background-color: #a978da;
color: white;
}
.app__input-btn-cont {
display: flex;
margin-top: 30px;
}
.app__input {
width: 80%;
box-sizing: border-box;
font-size: 20px;
padding: 3px 0 3px 10px;
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
border-right: none;
border: 1px solid #67B5E2;
background-color: #EDF9FF;
}
.app__input:focus {
outline: transparent;
}
.app__submit-btn {
display: block;
width: 20%;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
border-left: none;
background-color: #67B5E2;
border: 1px solid #67B5E2;
cursor: pointer;
font-size: 20px;
color: white;
transition: background-color 0.7s;
padding: 3px 0;
}
.app__submit-btn--edit {
background-color: #95CB5D;
}
.app__submit-btn:active {
width: 19.9%;
padding: 0 0;
}
.app__submit-btn:hover {
background-color: #377FB4;
}
.app__submit-btn--edit:hover {
background-color: #81AF51;
}
.app__items-container {
visibility: hidden;
/* transition: 0.7s; */
}
.app__items-container--visible {
visibility: visible;
}
.app__item {
display: flex;
justify-content: space-between;
margin: 20px 0;
}
.app__item:hover {
background-color: #b9e2fa;
border-radius: 10px;
}
.app__item-text {
padding-left: 10px;
font-size: 20px;
color: #1B5D81;
}
.app__item-btn-cont {
display: flex;
}
.app__item-btn-img {
width: 20px;
height: 20px;
}
.app__item-btn {
border: none;
background-color: transparent;
cursor: pointer;
display: block;
font-size: 18px;
}
.app__item-btn--edit {
margin-right: 45px;
color: #2c800f;
}
.app__item-btn--delete {
margin-right: 15px;
color: rgb(243, 117, 66);
}
.app__clear-btn {
display: block;
width: 150px;
margin: 20px auto;
border: none;
background-color: transparent;
font-size: 20px;
color: rgb(243, 117, 66);
letter-spacing: 2px;
cursor: pointer;
transition: border 0.3s;
border: 1px solid transparent;
}
.app__clear-btn:hover {
border: 1px solid rgb(243, 117, 66);
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8" name="viewport" content="width=device-width,
initial-scale=1">
<link rel="stylesheet" href="list.css">
<title>To Do List App</title>
</head>
<body>
<section class="app">
<form class="app__form">
<p class="app__alert"></p>
<h2 class="app__title">To Do List</h2>
<div class="app__input-btn-cont">
<input class="app__input" type="text" id="todo" placeholder="do stuff">
<button class="app__submit-btn">Submit</button>
</div>
</form>
<div class="app__items-container">
<div class="app__items-list">
</div>
<button class="app__clear-btn">Clear Items</button>
</div>
</section>
<script src="list.js"></script>
</body>
</html>
Your code is fine you just used the wrong comparison operator.
In your case you are comparing 2 IDs (operands) to see if they match up, so you should use normal operators such as (==, !=), but instead in your case, you have used strict operators which are used to compare the operand type and the operand itself.
You can learn more about Comparison Operators here.
Ultimatly,
In your function removeFromLocalStorage(id), you have an extra equal sign in your if function.
Instead of:
if (singleItem.id !== id) {
return singleItem;}
It should be:
if (singleItem.id != id) {
return singleItem;}
Hope this helps.

removing the input text in the text field that matches the removed tab

In this example I made, since it uses keyup event, each input text (separated by comma) entered is converted into a tab. I want the input text to be deleted from the text field according to the tab I remove; for example, I enter "Item 1" but I suddenly change my mind and decide to remove the "Item 1" tab, the input text in the text field that has a string that matches the textContent of the removed tab should be automatically deleted from the text field.
var query = document.querySelector.bind(document);
query('#textfield').addEventListener('keyup', addTag);
function addTag(e) {
var evt = e.target;
if(evt.value) {
var items = evt.value.split(',');
if(items.length <= 10) {
evt.nextElementSibling.innerHTML = null;
for(var i = 0; i < items.length; i++) {
if(items[i].length > 0) {
var label = document.createElement('label'),
span = document.createElement('span');
label.className = 'tag';
label.textContent = items[i];
span.className = 'remove';
span.title = 'Remove';
span.textContent = 'x';
label.insertAdjacentElement('beforeend', span);
evt.nextElementSibling.appendChild(label);
span.addEventListener('click', function() {
var currentElement = this;
currentElement.parentNode.parentNode.removeChild(currentElement.parentNode);
})
}
}
}
} else {
evt.nextElementSibling.innerHTML = null;
}
}
section {
width: 100%;
height: 100vh;
background: orange;
display: flex;
align-items: center;
justify-content: center;
}
.container {
width: 50%;
}
input[name] {
width: 100%;
border: none;
border-radius: 1rem 1rem 0 0;
font: 1rem 'Arial', sans-serif;
padding: 1rem;
background: #272727;
color: orange;
box-shadow: inset 0 0 5px 0 orange;
}
input[name]::placeholder {
font: 0.9rem 'Arial', sans-serif;
opacity: 0.9;
}
.tags {
width: 100%;
height: 250px;
padding: 1rem;
background: #dfdfdf;
border-radius: 0 0 1rem 1rem;
box-shadow: 0 5px 25px 0px rgba(0,0,0,0.4);
position: relative;
}
.tags > label {
width: auto;
display: inline-block;
background: #272727;
color: orange;
font: 1.1rem 'Arial', sans-serif;
padding: 0.4rem 0.6rem;
border-radius: .2rem;
margin: 5px;
}
.tags > label > span {
font-size: 0.7rem;
margin-left: 10px;
position: relative;
bottom: 2px;
color: #ff4d4d;
cursor: pointer;
}
<section id="tags-input">
<div class="container">
<input type="text" name="items" id="textfield" placeholder="Enter any item, separated by comma(','). Maximum of 10" autofocus>
<div class="tags"></div>
</div>
</section>
How can I make that feature possible?
Replace the 'x' button listener with this one:
span.addEventListener('click', function () {
var text_field = document.getElementById("textfield");
var evt = this.parentNode;
var tags = text_field.value;
this.parentNode.removeChild(this); // remove the 'x' span so you can get the pure tag text with .innerHTML
var evname = evt.innerHTML;
var tags_array = tags.split(",");
var tag_position = tags_array.indexOf(evname);
if(tag_position > -1)
tags_array.splice(tag_position,1);
text_field.value = tags_array.join(',');
evt.parentNode.removeChild(evt);
})
// Coding this complexity in pure javascript when there is jQuery is ... like eating soup with a fork. You will get the job done, but it is dammn hard!

Should I use sessionStorage and if so, how?

I have built a simple task app that allows you to add different tasks. It works fine. I am not sure what is the best approach however to retain the data/HTML once the page is refreshed. I have heard of HTML5 session/localStorage but I am not sure if this would be the best method to use in this situation. Also, I would need help making this work if sessionStorage was a good choice.
window.onload = init;
function init() {
var generateBtn = document.getElementById("generate");
generateBtn.onclick = addTask;
var tasksWrapper = document.getElementById("tasksWrapper");
var taskDesc = document.getElementById("taskDesc");
}
var taskId = 0;
var taskBarArray = [];
function addTask() {
taskId++;
var taskBar = document.createElement("div");
var taskBarInput = document.createElement("input");
var taskBarDeleteBtn = document.createElement("input");
taskBar.setAttribute("id", taskId);
taskBar.setAttribute("class", "taskBar");
taskBarInput.setAttribute("class", "taskDesc");
taskBarInput.setAttribute("type", "text");
taskBarInput.setAttribute("placeholder", "Enter task");
function rmPlaceholder() {
taskBarInput.removeAttribute("placeholder", "Enter task");
}
function addPlaceholder() {
taskBarInput.setAttribute("placeholder", "Enter task");
}
taskBarInput.onfocus = rmPlaceholder;
taskBarInput.onblur = addPlaceholder;
taskBarInput.setAttribute("name", "taskDesc");
taskBarInput.setAttribute("value", taskDesc.value);
taskBarDeleteBtn.setAttribute("class", "deleteBtn");
taskBarDeleteBtn.setAttribute("type", "button");
taskBarDeleteBtn.setAttribute("value", "x");
var addTaskBar = tasksWrapper.appendChild(taskBar);
var targetTaskId = document.getElementById(taskId);
var addTaskBarInput = targetTaskId.appendChild(taskBarInput);
var AddTaskBarDeleteBtn = targetTaskId.appendChild(taskBarDeleteBtn);
taskBarArray.push(taskBar);
taskDesc.value = "";
taskBarDeleteBtn.onclick = removeTask;
function removeTask(e) {
taskBarDeleteBtn = e.target;
tasksWrapper.removeChild(taskBar);
taskBarArray.pop(e);
if (taskBarArray.length < 1) {
taskId = 0;
}
}
}
#main_wrapper {
margin: 0 auto;
max-width: 528px;
width: 100%;
height: 20px;
}
.taskBar {
width: 100%;
background: #333230;
border-bottom: 1px solid #fff;
border-radius: 10px;
}
.taskDesc {
margin: 10px 0 10px 10px;
background: none;
border: none;
outline: none;
font-size: 20px;
color: #fff;
text-transform: uppercase;
z-index: 9999;
}
.deleteBtn {
margin: 6px 6px 0 0;
padding: 6px;
width: 32px;
background: #8F0A09;
font-size: 15px;
color: #fff;
border-radius: 100px;
border-color: #000;
float: right;
outline: none;
}
#header {
padding: 10px;
background: #000;
border-bottom: 1px solid #fff;
border-radius: 10px;
}
#taskDesc {
padding: 2px 0;
width: 50%;
font-size: 20px;
}
#generate {
padding: 5px 83px;
background: #82CC12;
font-size: 20px;
border-color: #000;
border-radius: 5px;
outline: none;
}
::-webkit-input-placeholder {
color: #4C4B48;
}
::-moz-placeholder {
color: #4C4B48;
}
:-ms-placeholder {
color: #4C4B48;
}
<div id="main_wrapper">
<div id="header">
<input type="text" id="taskDesc"></input>
<input type="button" id="generate" value="Add task">
</div>
<div id="tasksWrapper">
</div>
</div>
Here I would use localStorage, it will be remembered even after the session has timed out. A session is probably ended if the user restarts their browser.
The only problems I see with localStorage is the 10 MB size limit on desktops (2 MB om mobile devices I think), and that it's not easy enough to get data from localStorage to the server. But localStorage would be a perfect fit for a TODO app with simple items.

Categories

Resources