append various inputs to display it as a string - javascript

Hey I have the following problem, I want to display two text inputs as a list element but I do not know how to append various variables. The code below only shows the first input in the list.
function addLi () {
let x = document.createElement("LI");
let name= document.createTextNode(document.getElementById("name").value);
let city= document.createTextNode(document.getElementById("city").value);
x.appendChild(name)
document.getElementById("list").appendChild(x);
return false;
}

You could do it this way (just an example with a loop):
document.getElementById('add-btn').addEventListener('click', addLi);
function addLi() {
const items = ['name', 'city'];
for (let item of items) {
const li = document.createElement('li');
const value = document.createTextNode(document.getElementById(item).value);
li.appendChild(value);
document.getElementById("list").appendChild(li);
}
}
<input id="name" value="John Doe"> <input id="city" value="Sydney">
<button id="add-btn">Add to list</button>
<ul id="list"></ul>

Related

(JS) Remove an option from select box JUST ONCE after calling function

I have this code where I want to add text to the select box when calling a function via clicking an input button.
I want the select box to have a default text when the page is loaded and no value is added to the array. And I want this text to vanish but I could still add many values from the input box and make them show on the select box.
So I made the input and select box with the following:
let num = document.querySelector('input#numtxt')
let lista = document.querySelector('select#seltxt')
let res = document.querySelector('div#res')
let valores = []
function adicionar() {
if (isNumero(num.value) && !inLista(num.value, valores)) {
lista.options[0] = null //
valores.push(Number(num.value))
let item = document.createElement('option')
item.text = `Valor ${num.value} adicionado.`
lista.appendChild(item)
} else {
window.alert('Valor inválido ou já existe!')
}
}
<div>
<p>TYpe a number between 1 and 100: <input type="number" name="num1" id="numtxt">
<input type="button" value="Adicionar" onclick="adicionar()"></p>
</div>
<div>
<p>
<select name="sel1" id="seltxt" size="10">
<option>Type a number above!</option>
</select>
</p>
<p><input type="button" value="End" onclick="finalizar()"></p>
</div>
I've tried a lot of commands with boxvar.options[0] = null and boxvar.remove(0)but they all kept removing the first value which I need for the program.
Any sugestions?
let num = document.querySelector('input#numtxt')
let lista = document.querySelector('select#seltxt')
let res = document.querySelector('div#res')
let valores = []
function adicionar() {
if (isNumero(num.value) && !inLista(num.value, valores)) {
if(!valores.length) {
// If there are no values on list, delete whatever is inside of select
lista.innerHTML = ''
}
valores.push(Number(num.value))
let item = document.createElement('option')
item.text = `Valor ${num.value} adicionado.`
lista.appendChild(item)
} else {
window.alert('Valor inválido ou já existe!')
}
}
This is slightly verbose for clarity - if we add a data attribute we can filter on that and remove it if it exists. We can also filter by values and not add if the new one exists (it could be a data attribute if you do not want to set the value.
let lista = document.querySelector('#seltxt');
let res = document.querySelector('#res');
let valores = [];
function adicionar() {
let num = document.querySelector('#numtxt');
let opts = [...lista.options].filter((element, index) => {
return element.dataset.default == "default";
});
console.log(opts);
if (opts.length) {
opts[0].remove();
}
let newValue = Number(num.value);
// now if it already exists, don't add it
let matchOpt = [...lista.options].filter((element, index) => {
return element.value == newValue;
});
// we already have it so jump back out
if (matchOpt.length) {
return;
}
valores.push(newValue);
let item = document.createElement('option');
item.text = `Valor ${num.value} adicionado.`;
item.value = newValue;
lista.appendChild(item);
}
<div>
<p>Type a number between 1 and 100: <input type="number" name="num1" id="numtxt">
<input type="button" value="Adicionar" onclick="adicionar()"></p>
</div>
<div>
<p>
<select name="sel1" id="seltxt" size="10">
<option data-default="default">Type a number above!</option>
</select>
</p>
<p><input type="button" value="End" onclick="finalizar()"></p>
</div>

In a dynamic ordered list in pure js, with <ol> and <li> tags as inputs, i cannot access properties of object inside array

i know a little about .map, .key, .hasownproperty, and .value, but trying to avoid them (didn't learned them in a class).
any idea for the "keep-it-simple-stupid" solution here?
thanks a lot.
<html>
<body>
<form>
<input type="text" id="firstname">
<input type="text" id="lastname">
</form>
<button onclick="insertUser()">ins</button>
<div id="myContainer"></div>
</body>
<script>
let properties = [
'firstname',
'lastname',
]
let users = []
let form = document.querySelector('form')
let myParent = document.getElementById('myContainer')
function insertUser() {
let user = {}
for (let prop of properties) {
user[prop] = form.elements[prop].value
}
users.push(user)
form.reset()
for (let i = 0; i < users.length; i++){
console.log(users[i])//works, i see firstname and lastname
}
//console.log(users[0].firstname) works awesome
for (let key in user){
console.log(user[key].value)//gives me undefined in any possible variations
}
}
/*
myOl.innerHTML = "array item #"
myLi.innerHTML = "object inside array value"
myOl.appendChild(myLi)
myParent.appendChild(myOl)
*/
</script>
</html>

Increment and update value in the total number after insert new rows dynamically

EDIT: I have updated the code with the answers.
I have a increment function that is working fine. However:
1. I would like to set some limits based on the total number available in one of the span. For example, 10. So the incrementing can't be more than 10. #DONE
Another issue is that I am planning to have multiple rows and before I save I want to make sure if we count the increments in every row it should not be more than 10 as well. If it decrease the total number (span) dynamically would be nice.
I'm adding rows dynamically with the ADD button, how can I add news rows that actually work with the current functions? Mine rows just clone the first one and the increment function is disabled.
document.addEventListener('DOMContentLoaded', async function() {
document.querySelector('#addlocationdest').addEventListener('click', add);
});
function add() {
var x = 1;
var container = document.getElementById('destination');
var detail = document.getElementById('row');
var clone = detail.cloneNode(true);
clone.id = "destination" + x;
x++;
container.appendChild(clone);
}
window.addEventListener("load", () => {
let elTotalQuantity = document.querySelector("#totalqty");
let totalQuantity = parseInt(elTotalQuantity.innerHTML);
function getSumOfRows() {
let sum = 0;
for (let input of document.querySelectorAll("form .row > input.quantity"))
sum += parseInt(input.value);
return sum;
}
for (let row of document.querySelectorAll("form .row")) {
let input = row.querySelector("input");
row.querySelector(".increment").addEventListener("click", () => {
if (getSumOfRows() >= totalQuantity) return;
input.value++;
elTotalQuantity.innerHTML = totalQuantity - getSumOfRows();
});
row.querySelector(".decrement").addEventListener("click", () => {
if (input.value <= 0) return;
input.value--;
elTotalQuantity.innerHTML = totalQuantity - getSumOfRows();
});
}
});
<div id="location" class="hide">
<div class="title">Transfer details</div><br>
<div class="line padded-s">Total Quantity: <span>10</span></div>
<br>
<form>
<label>New Total Quantity at this location: <span id="totalqty">10</span></label>
<br>
<div id="destination">
<div id="row" class="row">
<button type="button" class="decrement">-</button>
<input type="text" class="quantity" value="0" readonly/>
<button type="button" class="increment">+</button>
<a>Location: </a>
<input type="text" class="location" value="0" readonly/>
</div>
</div>
</form>
<label>Total being transfer: <p id="total-sum"></p></label>
<br>
<button type="button" id="addlocationdest">ADD</button>
<button type="button" id="removelocationdest">REMOVE</button>
</div>
Prologue
As long as the total quantity is fixed at the beginning of the script-execution, this works. Otherwise, it would be best to save the actual allowed total quantity as an attribute, and observe it using a MutationObserver. That way you can update your max. value in your code dynamically, when the total quantity-attribute changes. You can define custom attributes by naming them "data-*" where "*" is a custom name.
Solution for your problem
You are using the same ID on multiple elements. What you meant were classes, so change id="increment" to class="increment", and the same for decrement.
Since we don't want to input something with the buttons, but add listener to them, I'd say it is better to actually use <button>. In forms, buttons act as type="submit", which we don't want, so we need to change it to type="button".
Since the rows and the total quantity actually belong together, it is wiser to place them together into one <form>-element. However, you can still group the buttons and inputs as a row together using <div>.
Now regarding the in-/decrementing of the row's values and the total quantity:
Save the allowed total quantity in a variable
Add event-listener to the corresponding buttons
If action is valid, change row's value
Update total quantity number to totalQuantity - getSumOfRows()
To add new rows dynamically, we create and setup such an element, and append it to the form. See the appendNewRow()-function below.
Sidenote
I have added the readonly attribute to the input-fields so that you cannot enter numbers via keyboard.
window.addEventListener("load", () => {
let elTotalQuantity = document.querySelector("#totalqty");
let totalQuantity = parseInt(elTotalQuantity.innerHTML);
function getSumOfRows() {
let sum = 0;
for (let input of document.querySelectorAll("form .row > input.quantity"))
sum += parseInt(input.value);
return sum;
}
function updateTotalQuantity() {
elTotalQuantity.innerHTML = totalQuantity - getSumOfRows();
}
function appendNewRow() {
let row = document.createElement("div");
row.classList.add("row");
let child;
// input.quantity
let input = document.createElement("input");
input.classList.add("quantity");
input.value = "0";
input.setAttribute("readonly", "");
input.setAttribute("type", "text");
row.append(input);
// button.increment
child = document.createElement("button");
child.classList.add("increment");
child.innerHTML = "+";
child.setAttribute("type", "button");
child.addEventListener("click", () => {
if (getSumOfRows() >= totalQuantity) return;
input.value++;
updateTotalQuantity();
});
row.append(child);
// button.increment
child = document.createElement("button");
child.classList.add("decrement");
child.innerHTML = "-";
child.setAttribute("type", "button");
child.addEventListener("click", () => {
if (input.value <= 0) return;
input.value--;
updateTotalQuantity();
});
row.append(child);
// button.remove-row
child = document.createElement("button");
child.classList.add("remove-row");
child.innerHTML = "Remove";
child.setAttribute("type", "button");
child.addEventListener("click", () => {
row.remove();
updateTotalQuantity();
});
row.append(child);
document.querySelector("form .rows").append(row);
}
document.querySelector("form .add-row").addEventListener("click", () => appendNewRow());
appendNewRow();
});
<form>
<label>Total Quantity: <span id="totalqty">10</span></label>
<br>
<div class="rows">
</div>
<button type="button" class="add-row">Add new row</button>
</form>
QuerySelector only selects the first occurrence so you haven't really added a listener to the second "row". You should use querySelectorAll but, instead of unique ids, use classes.
<input class="increment" type="button" value="+" />
Now you can use document.querySelectorAll(".increment") to get all elements in an array.
You can traverse in the DOM by using parentElement. By knowing which button you clicked, you can traverse up to the form element and then select the first child - which is an input. A more dynamic way would be to use querySelector to select the input, in case the HTML change in the future. Anyway, that's how you can know which input to manipulate based on where the buttons are in the DOM.
I added two global variables, totalSum and maxSum. maxSum is fetched from your span element (which I assigned an unique id to). totalSum makes sure that all inputs combined doesn't exceed maxSum.
You had some duplicate code, so I refactored it into a new method: changeValue.
In all, I think the code speaks for itself.
Oh, this code doesn't take into account that the user can change the value inside the input. I will leave that for you to figure out with an "oninput" listener on each text input.
var totalSum = 0; // 3
var maxSum = 0
var totalSumElement = null;
document.addEventListener('DOMContentLoaded', async function() {
totalSumElement = document.getElementById('total-sum');
maxSum = document.getElementById('max-sum').innerText;
var incrementElements = document.querySelectorAll('.increment'); // 1
var decrementElements = document.querySelectorAll('.decrement');
addListener('click', incrementElements, incrementValue);
addListener('click', decrementElements, decrementValue);
});
function addListener(type, elementArr, func) {
for (element of elementArr) {
element.addEventListener(type, func);
}
}
function withinRange(newValue) {
var maxReached = newValue > maxSum; // 3
var zeroReached = newValue < 0;
return !maxReached && !zeroReached;
}
function changeValue(event, change) { // 4
if (withinRange(totalSum + change)) {
let parent = event.currentTarget.parentElement; // 2
let input = parent.children[0];
let value = parseInt(input.value) || 0;
if (withinRange(value + change)) {
input.value = value + change;
totalSum = totalSum + change;
}
}
totalSumElement.textContent = `Total: ${totalSum}`;
}
function incrementValue(event) {
changeValue(event, 1);
}
function decrementValue(event) {
changeValue(event, -1);
}
#totalqty {
padding-bottom: 1rem;
}
<div id="totalqty" class="line padded-s">Total Quantity: <span id="max-sum">10</span></div>
<form>
<input type="text" value="0" />
<input class="increment" type="button" value="+" />
<input class="decrement" type="button" value="-" />
</form>
<form>
<input type="text" value="0" />
<input class="increment" type="button" value="+" />
<input class="decrement" type="button" value="-" />
</form>
<p id="total-sum"></p>

After creating array of objects how to update a key value of object? JavaScript

I got something simple but I'm obviously too stupid.
I have two inputs in my HTML. 1st is company and 2nd is shares. I'm creating UL with the companies. The thing is I want to do that when I enter a company name that already exist in the array I want to update the shares of that company only. I will share the code I made so far.
<body>
<form>
<input id="company" placeholder="Company" type="text" />
<input id="input" placeholder="Shares" type="number" />
<button id="btn">Add</button>
</form>
<ul id="content"></ul>
</body>
And the JavaScript code is here:
const button = document.getElementById("btn");
const content = document.getElementById("content");
let companiesArray = [];
function myCompany(e) {
e.preventDefault();
const inputShares = document.getElementById("input").value;
const inputCompany = document.getElementById("company").value;
let obj = {
company: inputCompany,
shares: inputShares,
};
for (const item of companiesArray) {
if (item.company === obj.company) {
//TO DO
console.log("Match");
return;
}
}
const li = document.createElement("li");
content.appendChild(li).textContent = `${obj.company} - ${obj.shares}`;
companiesArray = [...companiesArray, obj];
console.log(companiesArray);
}
button.addEventListener("click", myCompany);
So again, when I enter a unique company => li is created in the ul. If I enter the same company I just want to update the shares without adding new li.
if (item.company === obj.company) {
//TO DO
const list = document.getElementsByTagName('li')
const element = [...list].filter((item) => item.innerText.split(" ")[0] === obj.company)[0]
element.innerText = `${obj.company} - ${obj.shares}`
console.log("Match");
return;
}
Hope this will help :)

Is it possible to hide an empty <table> row with CSS?

I just starting learning the basics of JavaScript and HTML5. I'm trying to create a cookbook app where users can add recipes. These recipes are stored inside of an array and looped through a table with a for loop. The user can also edit the table. One of the main features is supposed to be that when a row of the table is empty, it automatically gets deleted. I tried using display: none with CSS, but it doesn't work. Here is my CSS for the table:
form.addEventListener("submit", function (event) {
let titleRow = table.insertRow();
let stepsRow = table.insertRow();
let titleCell = titleRow.insertCell();
let stepsCell = stepsRow.insertCell();
titleCell.innerHTML = inputTitle.value;
stepsCell.innerHTML = inputSteps.value;
titleCell.contentEditable = true;
stepsCell.contentEditable = true;
inputTitle.value = inputTitle.defaultValue;
inputSteps.value = inputSteps.defaultValue;
}, false);
td: empty {
display: none;
}
tr: empty {
display: none;
}
<form id="form">
<label>Insert Recipe: </label>
<textarea id = "inputTitle" rows="1" cols="50" placeholder="Recipe Title"></textarea>
<textarea id = "inputSteps" rows = "5" cols = "50" placeholder="Recipe Steps"></textarea>
<button type="submit">Add Recipe</button>
</form>
<table id = "table"></table>
The table element refers to an HTML5 table, inputTitle refers to one textarea, and inputSteps refers a different textarea.
I may have mistakes in my JavaScript.
Thanks in advance!
First, since you aren't submitting the data to any other resource, you shouldn't be using a form or a submit button.
Now, the simplest solution is to just perform validation on the inputs before building the table. if the input is empty, don't build those rows.
Lastly, don't use .innerHTML when the string you are working with doesn't contain any HTML. Use .textContent instead because .innerHTML has performance and security considerations.
let inputTitle = document.getElementById("inputTitle");
let inputSteps = document.getElementById("inputSteps");
document.querySelector("button").addEventListener("click", function (event) {
// First, just check to see if there was valid input
if(!inputTitle.value || !inputSteps.value){
alert("You must fill in a title and a recipe");
return; // Exit function
}
let titleRow = table.insertRow();
let stepsRow = table.insertRow();
let titleCell = titleRow.insertCell();
let stepsCell = stepsRow.insertCell();
titleCell.textContent = inputTitle.value;
stepsCell.textContent = inputSteps.value;
titleCell.contentEditable = true;
stepsCell.contentEditable = true;
});
<label>Insert Recipe: </label>
<textarea id = "inputTitle" rows="1" cols="50" placeholder="Recipe Title"></textarea>
<textarea id = "inputSteps" rows = "5" cols = "50" placeholder="Recipe Steps"></textarea>
<button type="button">Add Recipe</button>
<table id = "table"></table>

Categories

Resources