I am aiming to create a JS todo list similar to https://www.w3schools.com/howto/howto_js_todolist.asp
I have created the basic structure of my todo list application and the function of the close buttons.
These are working well, but I have a problem with adding new todo list items and checking and unchecking todo items.
I'm not sure if I'm using the classlist toggle property well, and also cannot figure why the add button doesn't work at all.
var todoitemlist = document.getElementsByClassName('todo-item');
var i;
for (i = 0; i < todoitemlist.length; i++) {
var span = document.createElement("SPAN");
span.innerHTML = "Close";
span.className = "closebutton";
todoitemlist[i].appendChild(span);
}
var close = document.getElementsByClassName("closebutton");
var i;
for (i = 0; i < close.length; i++) {
close[i].onclick = function() {
var listitem = this.parentElement;
listitem.style.display = "none";
}
}
var todoitemlistx = document.getElementsByClassName('todo-item');
//checked element
var i;
for (i = 0; i < todoitemlistx.length; i++) {
todoitemlistx[i].onclick = function(ev) {
ev.style.backgroundColor = "red";
ev.classList.toggle("todo-item-checked");
}
}
//add another list item
function add() {
var listitem = document.createElement("LI");
listitem.className = "todo-item";
var text = document.getElementById('todoinput').value;
var myul = getElementById('todo-list');
var t = document.createTextNode(text);
listitem.appendChild(t);
myul.appendChild(listitem);
var span = document.createElement("SPAN");
span.innerHTML = "Close";
span.className = "closebutton";
listitem[i].appendChild(span);
for (i = 0; i < close.length; i++) {
close[i].onclick = function() {
var div = this.parentElement;
div.style.display = "none";
}
}
}
* {
box-sizing: border-box;
}
body {
text-align: center;
margin: 0;
padding: 0;
background-color: #dbf9fc;
font-family: Arial, Helvetica, sans-serif;
color: rgba(0, 27, 39);
}
#todoinput {
margin: 5px;
padding: 5px;
width: 65%;
}
#add-button {
padding: 5px;
margin: 5px;
width: 5%;
background-color: rgba(0, 27, 39);
color: #dbf9fc;
border: none;
border-radius: 5px;
height: 1fr;
cursor: pointer;
}
#add-button:hover {
background-color: black;
}
#todo-list {
display: inline-block;
margin: 0;
padding: 0;
list-style: none;
width: 70%;
}
.todo-item {
position: relative;
display: flex;
justify-content: flex-start;
background-color: white;
border: 2px solid black;
padding: 5px;
margin: 5px;
}
.closebutton {
cursor: pointer;
justify-self: flex-end;
background-color: #e6772d;
position: absolute;
right: 0;
top: 0;
color: white;
float: right;
padding: 5px;
width: 30%;
margin: 0;
}
.closebutton:hover {
background-color: #c46526;
}
.todo-item-checked {
position: relative;
display: flex;
justify-content: flex-start;
background-color: rgb(187, 187, 187);
border: 2px solid black;
padding: 5px;
margin: 5px;
text-decoration: line-through;
}
.black {
background-color: black;
}
<main class="centered">
<h1>ToDo List JS</h1>
<h3>js project</h3>
<form action="">
<input type="text" name="" id="todoinput" placeholder="Enter the activity you've wented to do">
<input type="button" value="Add" id="add-button" onclick="add()">
</form>
<ul id="todo-list">
<li class="todo-item">
Hit the lights
</li>
<li class="todo-item">
Hit the lights
</li>
<li class="todo-item">
Hit the lights
</li>
<li class="todo-item-checked todo-item">
Hit the lights
</li>
</ul>
</main>
I remember what it was like starting development and how hard it could be, so I persevered with this.
I thought it would be a few small changes but it turned into a massive rewrite.
Una grande padulo, as the Italians like to say.
I hope you can or try to understand what I did here.
The big lesson would be, don't write the same code twice, put it in a separate function.
So this seems to work.
Let me know if it doesn't.
var todoitemlist=document.getElementsByClassName('todo-item');
for(var i=0;i<todoitemlist.length;i++)
{
myawesomeclosebutton(todoitemlist[i]);
todoitemlist[i].addEventListener('click',myawesomebackground);
}
function myawesomeclosebutton(myawesomeitem)
{
var span=document.createElement('span');
span.innerHTML='Close';
span.className='closebutton';
span.addEventListener('click',myawesomecloseevent);
myawesomeitem.appendChild(span);
}
function myawesomebackground(ev)
{
if(ev.target.style.backgroundColor!='red')
{
ev.target.style.backgroundColor='red';
}
else
{
ev.target.style.backgroundColor='transparent';
}
}
function myawesomecloseevent(event)
{
var div=event.target.parentElement;
div.style.display='none';
}
function add()
{
var listitem=document.createElement('li');
listitem.className='todo-item';
var myawesomeinput=document.getElementById('todoinput');
var text=myawesomeinput.value;
var myul=document.getElementById('todo-list');
var t=document.createTextNode(text);
listitem.appendChild(t);
myul.appendChild(listitem);
listitem.addEventListener('click',myawesomebackground);
myawesomeclosebutton(listitem);
myawesomeinput.value='';
}
Related
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>
So I have this page:
I have a list that is made from items in array. If user adds new food the input should go to the array and show new item in the list and the input field should dissapear. Right now I am getting 404 error.
How can I do this?
// find elements
var banner = $("#banner-message")
var button = $("button")
var select = document.getElementById("selectFood");
var options = ["Pizza", "Hambruger", "Salad"];
for (var i = 0; i < options.length; i++) {
var opt = options[i];
var el = document.createElement("option");
el.textContent = opt;
el.value = opt;
select.appendChild(el);
}
function foodInput() {
boxvalue = document.getElementById('addedFood').value;
options.push(boxvalue);
}
function addFunction() {
var x = document.getElementById("myDIV");
if (x.style.display === "block") {
x.style.display = "none";
} else {
x.style.display = "block";
}
}
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#banner-message {
background: #778899;
border-radius: 4px;
padding: 20px;
font-size: 25px;
text-align: center;
transition: all 0.2s;
margin: 0 auto;
width: 300px;
}
button {
background: #A52A2A;
border: none;
border-radius: 5px;
padding: 8px 14px;
font-size: 15px;
color: #fff;
}
.addButton {
background: #A52A2A;
color: white;
}
#banner-message.alt {
background: #0084ff;
color: #fff;
margin-top: 40px;
width: 200px;
}
#banner-message.alt button {
background: #fff;
color: #000;
}
.hide {
display: none;
}
.show {
display: block;
}
.demo {
background: #A52A2A;
border: none;
border-radius: 5px;
padding: 8px 14px;
font-size: 15px;
color: #fff;
}
#myDIV {
background: #778899;
padding: 20px;
font-size: 25px;
text-align: center;
transition: all 0.2s;
margin: 0 auto;
width: 300px;
display: none;
}
input[type=submit] {
background: #A52A2A;
border: none;
border-radius: 5px;
padding: 8px 14px;
font-size: 15px;
color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="banner-message">
<p>My favorite food</p>
<select id="selectFood">
<option>Select food</option>
</select>
<button>Choose</button>
<br>
<button id="addButton" onclick="addFunction();">
Add new food
</button>
</div>
<div id="myDIV">
<form onsubmit="return foodInput()">
<input type="text" id="addedFood" placeholder="Type food">
<input type="submit" value="Submit" onclick="addFunction()">
</form>
</div>
Actually, I think the problem was with the form trying to reload the fiddle Html page on submit.
Here is the working link of the modified content with some little optimization.
[https://jsfiddle.net/8rvax5z0/3/]
You can add a new method to refresh options after input like:
function refreshFoodInput(options) {
select.removeChild("option");
for (var i = 0; i < options.length; i++) {
var opt = options[i];
var el = document.createElement("option");
el.textContent = opt;
el.value = opt;
select.appendChild(el);
}
}
Then call this method in foodInput method like
function foodInput() {
var boxvalue = document.getElementById('addedFood').value;
options.push(boxvalue);
refreshFoodInput(options);
}
Now your whole code will be like:
// find elements
var banner = $("#banner-message")
var button = $("button")
var select = document.getElementById("selectFood");
var options = ["Pizza", "Hambruger", "Salad"];
refreshFoodInput(options);
function refreshFoodInput(options) {
select.removeChild("option");
for (var i = 0; i < options.length; i++) {
var opt = options[i];
var el = document.createElement("option");
el.textContent = opt;
el.value = opt;
select.appendChild(el);
}
}
function foodInput() {
var boxvalue = document.getElementById('addedFood').value;
options.push(boxvalue);
refreshFoodInput(options);
}
function addFunction() {
var x = document.getElementById("myDIV");
if (x.style.display === "block") {
x.style.display = "none";
} else {
x.style.display = "block";
}
}
Hope you get it worked.
Enjoy!
var options = ["Pizza", "Hambruger", "Salad"];
document.querySelector("form#addFoodForm").onsubmit=function(){
let existingFoods=document.querySelector("#selectFood");
const newFood=this.querySelector("input#addedFood").value;
const newFoodOption=document.createElement("option");
newFoodOption.setAttribute("value", newFood);
newFoodOption.innerHTML=newFood;
existingFoods.append(newFoodOption);
refreshFoodOptions(); // This function will add the new foods into the global 'options' array !
return false;
}
function refreshFoodOptions(){
const selectInpt=document.querySelector("#selectFood");
selectInpt.querySelectorAll("option[value]").forEach(function(food){
if(!in_array(food.value, options)) options.push(food.value);
});
console.log( options );
return options;
}
function in_array(key, array){
let exists=false;
array.forEach(function(arrayKey){
if(arrayKey==key) exists=true;
});
return exists;
}
<div id="banner-message">
<p>My favorite food</p>
<select id="selectFood">
<option>Select food</option>
</select>
<button>Choose</button>
</div>
<div id="myDIV">
<form id="addFoodForm">
<input type="text" id="addedFood" placeholder="Type food">
<input type="submit" value="Submit">
</form>
</div>
I could completely redo your code to modernize and reduce it but this is not the idea of the question then I just fixed with this:
Call the options generator (for) when you finishing the push on array because you need to display the new array (select options).
To do it you need to create a function that executes the for and call on first load and every time when new food was added.
Empty the select before add options to prevent duplicated options.
Se the code below:
// find elements
var banner = $("#banner-message")
var button = $("button")
var select = document.getElementById("selectFood");
var options = ["Select Food", "Pizza", "Hambruger", "Salad"];
function generateSeletc(foodArray) {
$('#selectFood').empty();
for (var i = 0; i < options.length; i++) {
var opt = options[i];
var el = document.createElement("option");
el.textContent = opt;
el.value = opt;
select.appendChild(el);
}
}
function foodInput(event) {
event.preventDefault();
boxvalue = document.getElementById('addedFood').value;
options.push(boxvalue);
generateSeletc(options);
}
function addFunction() {
var x = document.getElementById("myDIV");
if (x.style.display === "block") {
x.style.display = "none";
} else {
x.style.display = "block";
}
}
generateSeletc(options);
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#banner-message {
background: #778899;
border-radius: 4px;
padding: 20px;
font-size: 25px;
text-align: center;
transition: all 0.2s;
margin: 0 auto;
width: 300px;
}
button {
background: #A52A2A;
border: none;
border-radius: 5px;
padding: 8px 14px;
font-size: 15px;
color: #fff;
}
.addButton {
background: #A52A2A;
color: white;
}
#banner-message.alt {
background: #0084ff;
color: #fff;
margin-top: 40px;
width: 200px;
}
#banner-message.alt button {
background: #fff;
color: #000;
}
.hide {
display: none;
}
.show {
display: block;
}
.demo {
background: #A52A2A;
border: none;
border-radius: 5px;
padding: 8px 14px;
font-size: 15px;
color: #fff;
}
#myDIV {
background: #778899;
padding: 20px;
font-size: 25px;
text-align: center;
transition: all 0.2s;
margin: 0 auto;
width: 300px;
display: none;
}
input[type=submit] {
background: #A52A2A;
border: none;
border-radius: 5px;
padding: 8px 14px;
font-size: 15px;
color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="banner-message">
<p>My favorite food</p>
<select id="selectFood">
<option>Select food</option>
</select>
<button>Choose</button>
<br>
<button id="addButton" onclick="addFunction();">
Add new food
</button>
</div>
<div id="myDIV">
<form onsubmit="return foodInput(event)">
<input type="text" id="addedFood" placeholder="Type food">
<input type="submit" value="Submit" onclick="addFunction()">
</form>
</div>
This is my first piece of code js done on my own. I am trying to have an input field to add items to the list and then if you press the button to generate code it will collect all the checked items and copy the text into another div.
Basically, my question is around two variables:
const list = listUl.children;
const listCopy = listUl.querySelectorAll('span');
Let say I have 4 items in the list. If I add a new item to the list I can see the list.length add this new item, it changes from 4 to 5.
But it doesn't happen with listCopy.length the value keeps being 4.
Why is it happening if lstCopy is inside of list?
How can I have listCopy updated too?
Here is my code:
const addItemInput = document.querySelector('.addItemInput');
const addItemButton = document.querySelector('.addItemButton');
const copyText = document.querySelector('.generateCode');
const listUl = document.querySelector('.list');
const list = listUl.children;
const listCopy = listUl.querySelectorAll('span');
const clonedCode = document.querySelector('.code p');
//FUNCTION: Generate value/items = Draggable, Checkbox, Remove button
const attachItemListButton = (item) => {
//Draggable
item.draggable = "true";
item.classList.add("list--item");
//Checkbox
let checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.className = 'checkbox';
checkbox.name = "chkboxName1";
checkbox.value = "value";
checkbox.id = "id";
item.insertBefore(checkbox, item.childNodes[0] || null);
//Remove button
let remove = document.createElement('button');
remove.className = 'remove';
remove.textContent = 'x';
item.appendChild(remove);
};
for (let i = 0; i < list.length; i += 1) {
attachItemListButton(list[i]);
}
//Cloning code if there are checked items
copyText.addEventListener('click', () => {
let copyTextFromList = "";
for (let i = 0; i < listCopy.length; i += 1) {
if (listCopy[i].parentNode.querySelector("input:checked")) {
copyTextFromList += listCopy[i].textContent + ',';
}
}
clonedCode.innerHTML = copyTextFromList;
});
//Add item from the input field to the list
addItemButton.addEventListener('click', () => {
let li = document.createElement('li');
let span = document.createElement('span');
span.textContent = addItemInput.value;
listUl.appendChild(li);
li.appendChild(span);
attachItemListButton(li);
addItemInput.value = '';
});
//FUNCTION: Remove button
listUl.addEventListener('click', (event) => {
if (event.target.tagName == 'BUTTON') {
if (event.target.className == 'remove') {
let li = event.target.parentNode;
let ul = li.parentNode;
ul.removeChild(li);
}
}
});
/* Google fonts */
#import url('https://fonts.googleapis.com/css?family=Heebo:300,400,700');
/* Root */
:root {
--color-white: #fff;
--color-black: #2D3142;
--color-black-2: #0E1116;
--color-gray: #CEE5F2;
--color-gray-2: #ACCBE1;
--color-gray-3: #CEE5F2;
--color-green: #439775;
--color-blue: #4686CC;
}
body {
font-family: 'Heebo', sans-serif;
font-weight: 400;
font-size: 16px;
color: black;
}
h2 {
font-weight: 700;
font-size: 1.5rem;
}
h3 {
font-weight: 700;
font-size: 1.25rem;
}
button {
background: var(--color-blue);
padding: 5px 10px;
border-radius: 5px;
color: var(--color-white);
}
[draggable] {
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
user-select: none;
-khtml-user-drag: element;
-webkit-user-drag: element;
}
ul.list {
list-style-type: none;
padding: 0;
max-width: 300px;
}
.list button {
background: var(--color-black);
}
.list--item {
display: flex;
justify-content: space-between;
align-items: center;
width: auto;
margin: 5px auto;
padding: 5px;
cursor: move;
background: var(--color-gray);
border-radius: 5px;
}
.list--item.draggingElement {
opacity: 0.4;
}
.list--item.over {
border-top: 3px solid var(--color-green);
}
button.remove {
margin: auto 0 auto auto;
}
input#id {
margin: auto 5px auto 0;
}
.button-wrapper {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
max-width: 300px;
}
.button-wrapper .addItemInput {
width: 63%;
border-radius: 5px 0 0 5px;
}
.button-wrapper .addItemButton {
width: 35%;
border-radius: 0 5px 5px 0;
}
.button-wrapper .generateCode {
width: 100%;
background: var(--color-green);
margin-top: 5px;
}
.code p {
background: var(--color-gray); padding: 5px;
border: 1px solid var(--color-gray-2);
min-height: 20px;
font-weight: 300;
}
<ul class="list">
<li><span>Header</span></li>
<li><span>Hero</span></li>
<li><span>Intro</span></li>
<li><span>Footer</span></li>
</ul>
<div class="button-wrapper">
<input type="text" class="addItemInput" placeholder="Item description">
<button class="addItemButton">Add item</button>
<button class="generateCode">Generate code</button>
</div>
<div class="code">
<h2>Code</h2>
<p></p>
</div>
There are two variants of NodeList, live and non-live ones. querySelectorAll returns a static NodeList that is not live. .children returns a live one (technically it returns an HTMLCollection but you can ignore this distinction for now).
To make listCopy be live as well, you could use listUl.getElementsByTagName('span')…
To select elements by their classes, use getElementsByClassName. There is no way (that I know of) to get a live collection with CSS or XPath (i.e. more complex) queries, though.
The problem is that const listCopy = listUl.querySelectorAll('span'); is initiated with the span array from the beginning.
In order to get updated list
const listCopy = listUl.querySelectorAll('span'); will be let listCopy = listUl.querySelectorAll('span'); and in your function
//Cloning code if there are checked items
copyText.addEventListener('click', () => {
// add the following line - in this way you will select the span from the updated list
listCopy = listUl.querySelectorAll('span');
let copyTextFromList = "";
for (let i = 0; i < listCopy.length; i += 1) {
if (listCopy[i].parentNode.querySelector("input:checked")) {
copyTextFromList += listCopy[i].textContent + ',';
}
}
clonedCode.innerHTML = copyTextFromList;
});
if you want to use querySelectorAll, this maybe help. with this every time you check the length it recalculates and returns the value. Symbol.iterator helps you to manipulate for...of loops.
const addItemInput = document.querySelector('.addItemInput');
const addItemButton = document.querySelector('.addItemButton');
const copyText = document.querySelector('.generateCode');
const listUl = document.querySelector('.list');
const list = listUl.children;
const listCopy = {
get length() {
return listUl.querySelectorAll('span').length
},
*[Symbol.iterator]() {
let i = 0;
const l = listUl.querySelectorAll('span');
while( i < l.length ) {
yield l[i];
i++;
}
}
};
const clonedCode = document.querySelector('.code p');
//FUNCTION: Generate value/items = Draggable, Checkbox, Remove button
const attachItemListButton = (item) => {
//Draggable
item.draggable = "true";
item.classList.add("list--item");
//Checkbox
let checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.className = 'checkbox';
checkbox.name = "chkboxName1";
checkbox.value = "value";
checkbox.id = "id";
item.insertBefore(checkbox, item.childNodes[0] || null);
//Remove button
let remove = document.createElement('button');
remove.className = 'remove';
remove.textContent = 'x';
item.appendChild(remove);
};
for (let i = 0; i < list.length; i += 1) {
attachItemListButton(list[i]);
}
//Cloning code if there are checked items
copyText.addEventListener('click', () => {
let copyTextFromList = "";
for (let item of listCopy) {
if (item.parentNode.querySelector("input:checked")) {
copyTextFromList += item.textContent + ',';
}
}
clonedCode.innerHTML = copyTextFromList;
});
//Add item from the input field to the list
addItemButton.addEventListener('click', () => {
let li = document.createElement('li');
let span = document.createElement('span');
span.textContent = addItemInput.value;
listUl.appendChild(li);
li.appendChild(span);
attachItemListButton(li);
addItemInput.value = '';
});
//FUNCTION: Remove button
listUl.addEventListener('click', (event) => {
if (event.target.tagName == 'BUTTON') {
if (event.target.className == 'remove') {
let li = event.target.parentNode;
let ul = li.parentNode;
ul.removeChild(li);
}
}
});
/* Google fonts */
#import url('https://fonts.googleapis.com/css?family=Heebo:300,400,700');
/* Root */
:root {
--color-white: #fff;
--color-black: #2D3142;
--color-black-2: #0E1116;
--color-gray: #CEE5F2;
--color-gray-2: #ACCBE1;
--color-gray-3: #CEE5F2;
--color-green: #439775;
--color-blue: #4686CC;
}
body {
font-family: 'Heebo', sans-serif;
font-weight: 400;
font-size: 16px;
color: black;
}
h2 {
font-weight: 700;
font-size: 1.5rem;
}
h3 {
font-weight: 700;
font-size: 1.25rem;
}
button {
background: var(--color-blue);
padding: 5px 10px;
border-radius: 5px;
color: var(--color-white);
}
[draggable] {
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
user-select: none;
-khtml-user-drag: element;
-webkit-user-drag: element;
}
ul.list {
list-style-type: none;
padding: 0;
max-width: 300px;
}
.list button {
background: var(--color-black);
}
.list--item {
display: flex;
justify-content: space-between;
align-items: center;
width: auto;
margin: 5px auto;
padding: 5px;
cursor: move;
background: var(--color-gray);
border-radius: 5px;
}
.list--item.draggingElement {
opacity: 0.4;
}
.list--item.over {
border-top: 3px solid var(--color-green);
}
button.remove {
margin: auto 0 auto auto;
}
input#id {
margin: auto 5px auto 0;
}
.button-wrapper {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
max-width: 300px;
}
.button-wrapper .addItemInput {
width: 63%;
border-radius: 5px 0 0 5px;
}
.button-wrapper .addItemButton {
width: 35%;
border-radius: 0 5px 5px 0;
}
.button-wrapper .generateCode {
width: 100%;
background: var(--color-green);
margin-top: 5px;
}
.code p {
background: var(--color-gray); padding: 5px;
border: 1px solid var(--color-gray-2);
min-height: 20px;
font-weight: 300;
}
<ul class="list">
<li><span>Header</span></li>
<li><span>Hero</span></li>
<li><span>Intro</span></li>
<li><span>Footer</span></li>
</ul>
<div class="button-wrapper">
<input type="text" class="addItemInput" placeholder="Item description">
<button class="addItemButton">Add item</button>
<button class="generateCode">Generate code</button>
</div>
<div class="code">
<h2>Code</h2>
<p></p>
</div>
What I want: User types word into input bar -> user presses Add button -> word is added to two lists "unsortedUL" and "sortedUL" - > user presses Sort button -> the list "sortedUL" gets sorted by descending (z-a), while "unsortedUL" remains exactly how the user inputted it.
I cannot figure out how to get TWO lists while only ONE of them is sorted.
var myNodelist = document.getElementsByTagName("LI");
var i;
for (i = 0; i < myNodelist.length; i++) {
var span = document.createElement("SPAN");
var txt = document.createTextNode("\u00D7");
span.className = "close";
span.appendChild(txt);
myNodelist[i].appendChild(span);
}
var close = document.getElementsByClassName("close");
var i;
for (i = 0; i < close.length; i++) {
close[i].onclick = function() {
var div = this.parentElement;
div.style.display = "none";
}
}
function newElement() {
var li = document.createElement("li");
var inputValue = document.getElementById("myInput").value;
var t = document.createTextNode(inputValue);
li.appendChild(t);
if (inputValue === '') {
alert("You must write a word!");
} else {
document.getElementById("sortedUL").appendChild(li);
}
document.getElementById("myInput").value = "";
var span = document.createElement("SPAN");
var txt = document.createTextNode("\u00D7");
span.className = "close";
span.appendChild(txt);
li.appendChild(span);
for (i = 0; i < close.length; i++) {
close[i].onclick = function() {
var div = this.parentElement;
div.style.display = "none";
}
}
}
function sortList() {
var list, i, switching, b, shouldSwitch;
list = document.getElementById("sortedUL");
switching = true;
while (switching) {
switching = false;
b = list.getElementsByTagName("LI");
for (i = 0; i < (b.length - 1); i++) {
shouldSwitch = false;
if (b[i].innerHTML.toLowerCase() < b[i + 1].innerHTML.toLowerCase()) {
shouldSwitch= true;
break;
}
}
if (shouldSwitch) {
b[i].parentNode.insertBefore(b[i + 1], b[i]);
switching = true;
}
}
}
document.getElementById("date").innerHTML = new Date().toDateString();
document.getElementById("time").innerHTML = new Date().toLocaleTimeString();
body {
margin: 0;
min-width: 250px;
background-color: green;
}
* {
box-sizing: border-box;
}
ul {
margin: 0;
padding: 0;
width: 100%;
float: right;
}
ul li {
cursor: pointer;
position: relative;
padding: 12px 8px 12px 40px;
list-style-type: number;
background: #eee;
font-size: 18px;
transition: 0.2s;
text-align: center;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.close {
position: absolute;
right: 0;
top: 0;
padding: 12px 16px 12px 16px;
}
.header {
background-color: green;
padding: 30px 40px;
color: white;
text-align: center;
}
.header:after {
content: "";
display: table;
clear: both;
}
input {
border: none;
width: 50%;
padding: 10px;
float: center;
font-size: 16px;
}
.addBtn {
padding: 10px;
width: 10%;
background: #d9d9d9;
color: #555;
float: right;
text-align: center;
font-size: 16px;
cursor: pointer;
transition: 0.3s;
}
.sortBtn {
padding: 10px;
width: 10%;
background: #d9d9d9;
color: #555;
float: left;
text-align: center;
font-size: 16px;
cursor: pointer;
transition: 0.3s;
}
<!DOCTYPE html>
<html>
<title>Assignment Two</title>
<body>
<h1 style="color:white;"align="center"id="date"></h1>
<h1 style="color:white;"align="center"id="time"></h1>
<div id="myDIV" class="header">
<h2 style="margin:5px">Enter a list of words</h2>
<input type="text" id="myInput" placeholder="Word...">
<span onclick="newElement()" class="addBtn">Add</span>
<span onclick="sortList()" class="sortBtn">Sort</span>
</div>
<ul id="sortedUL">
</ul>
<ul id="unsortedUL">
</ul>
</body>
</html>
You have to clone the HTML Node to append it twice.
Or create it twice like I did.
var myNodelist = document.getElementsByTagName("LI");
var i;
for (i = 0; i < myNodelist.length; i++) {
var span = document.createElement("SPAN");
var txt = document.createTextNode("\u00D7");
span.className = "close";
span.appendChild(txt);
myNodelist[i].appendChild(span);
}
var close = document.getElementsByClassName("close");
var i;
for (i = 0; i < close.length; i++) {
close[i].onclick = function() {
var div = this.parentElement;
div.style.display = "none";
}
}
function newElement() {
if (inputValue === '') {
alert("You must write a word!");
} else {
var li = document.createElement("li");
var inputValue = document.getElementById("myInput").value;
var t = document.createTextNode(inputValue);
li.appendChild(t);
document.getElementById("sortedUL").appendChild(li);
var li = document.createElement("li");
var inputValue = document.getElementById("myInput").value;
var t = document.createTextNode(inputValue);
li.appendChild(t);
document.getElementById("unsortedUL").appendChild(li);
}
document.getElementById("myInput").value = "";
var span = document.createElement("SPAN");
var txt = document.createTextNode("\u00D7");
span.className = "close";
span.appendChild(txt);
li.appendChild(span);
for (i = 0; i < close.length; i++) {
close[i].onclick = function() {
var div = this.parentElement;
div.style.display = "none";
}
}
}
function sortList() {
var list, i, switching, b, shouldSwitch;
list = document.getElementById("sortedUL");
switching = true;
while (switching) {
switching = false;
b = list.getElementsByTagName("LI");
for (i = 0; i < (b.length - 1); i++) {
shouldSwitch = false;
if (b[i].innerHTML.toLowerCase() < b[i + 1].innerHTML.toLowerCase()) {
shouldSwitch= true;
break;
}
}
if (shouldSwitch) {
b[i].parentNode.insertBefore(b[i + 1], b[i]);
switching = true;
}
}
}
document.getElementById("date").innerHTML = new Date().toDateString();
document.getElementById("time").innerHTML = new Date().toLocaleTimeString();
body {
margin: 0;
min-width: 250px;
background-color: green;
}
* {
box-sizing: border-box;
}
p {
font-size: 16px;
margin-left: 20px;
color: white;
text-transform: uppercase;
}
ul {
margin: 0 0 20px 0;
padding: 0;
width: 100%;
float: right;
}
ul li {
cursor: pointer;
position: relative;
padding: 12px 8px 12px 40px;
list-style-type: number;
background: #eee;
font-size: 18px;
transition: 0.2s;
text-align: center;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.close {
position: absolute;
right: 0;
top: 0;
padding: 12px 16px 12px 16px;
}
.header {
background-color: green;
padding: 30px 40px;
color: white;
text-align: center;
}
.header:after {
content: "";
display: table;
clear: both;
}
input {
border: none;
width: 50%;
padding: 10px;
float: center;
font-size: 16px;
}
.addBtn {
padding: 10px;
width: 10%;
background: #d9d9d9;
color: #555;
float: right;
text-align: center;
font-size: 16px;
cursor: pointer;
transition: 0.3s;
}
.sortBtn {
padding: 10px;
width: 10%;
background: #d9d9d9;
color: #555;
float: left;
text-align: center;
font-size: 16px;
cursor: pointer;
transition: 0.3s;
}
<!DOCTYPE html>
<html>
<title>Assignment Two</title>
<body>
<h1 style="color:white;"align="center"id="date"></h1>
<h1 style="color:white;"align="center"id="time"></h1>
<div id="myDIV" class="header">
<h2 style="margin:5px">Enter a list of words</h2>
<input type="text" id="myInput" placeholder="Word...">
<span onclick="newElement()" class="addBtn">Add</span>
<span onclick="sortList()" class="sortBtn">Sort</span>
</div>
<p>Sorted</p>
<ul id="sortedUL">
</ul>
<p>Unsorted</p>
<ul id="unsortedUL">
</ul>
</body>
</html>
While you need list you can use Javascript Array
Here you can have two Arrays which would be SortedList and UnsortedList
I have declare both the list globally so that you can sort one list and keep one list without change
Refer The Below Code for the Work Flow
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form>
<div>
<input type="text" name="txtName" id="txtName"/>
<input type="button" value="Add" onclick="AddToList()"/>
<input type="button" value="Sort" onclick="SortList()"/>
</div>
</form>
</body>
</html>
<script>
var sortedList = [];
var unsortedList = [];
function AddToList() {
var data = document.getElementById("txtName").value;
sortedList.push(data);
unsortedList.push(data);
}
function SortList() {
sortedList.sort();
sortedList.reverse();
console.log(sortedList);
console.log(unsortedList);
}
</script>
Here I have created two buttons as you said
And Called a function to sort and other to add in the List.
As you said you need the Unsorted List to be as it is, So in the SortList() function we have printed sortedList and unsortedList Both two see a diffrence.
As expected sortedList will print the descending order data and unsortedList will print normal data.
You just need to insert it into both lists as each word is added, i.e. where you have:
document.getElementById("sortedUL").appendChild(li);
you should add a second line like this:
document.getElementById("unsortedUL").appendChild(li.cloneNode(true));
The node cloning might be what you were missing if you tried it before, otherwise it would move the same element and it ends up in only one list. The 'true' argument makes a deep copy so that the text node underneath it is copied as well.
Incidentally, this whole operation would be a lot easier with jQuery, it's the kind of DOM manipulation that the library was meant for. However people jump to jQuery so quickly and it's good that you are doing it with vanilla JavaScript.
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!