I have dropdown list where options are filling with values inside js code. After click on submit button, I want my dropdown to be changed for updated values, but it will just add new values to already existed values.
My idea was in deleting all options before they are creating, so options with new values will be added from a scratch.
So I tried to receive the length of datalist, but something like var x = document.getElementById("browsers").options.length; from w3schools doesn't work.
In addition, I tried to receive all options with setting className for option and then get all options with const options = document.getElementsByClassName('datalist-option') and with this try I received all options, but then I can't iterate through them to do the following way:
const options = document.getElementsByClassName('datalist-option')
options.forEach(option => option.value = '')
which returns me error:
Uncaught TypeError: options.forEach is not a function
at HTMLButtonElement.<anonymous>
Code:
const input = document.querySelector('#input')
const datalist = document.querySelector('#datalist')
const submitButton = document.querySelector('#submit-button')
const values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
values.forEach(value => {
const option = document.createElement('option')
option.value = value
datalist.appendChild(option)
})
submitButton.addEventListener('click', (e) => {
e.preventDefault()
values.splice(0, 5)
values.forEach(value => {
const option = document.createElement('option')
option.value = value
datalist.appendChild(option)
})
})
<!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>Document</title>
<script src="./test.js" defer></script>
</head>
<body>
<input type="text" list="datalist" id="input">
<datalist id="datalist"></datalist>
<button type="submit" id="submit-button">Submit</button>
</body>
</html>
Actually this question wasn't published just me to answer it, but actually I've found a solution.
I iterated through datalist children which returns HTMLCollection
for (let option of datalist.children)
option.value = ''
And now code works as it has to
const input = document.querySelector('#input')
const datalist = document.querySelector('#datalist')
const submitButton = document.querySelector('#submit-button')
const values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
values.forEach(value => {
const option = document.createElement('option')
option.value = value
option.id = 'datalist-option'
datalist.appendChild(option)
})
submitButton.addEventListener('click', (e) => {
e.preventDefault()
values.splice(0, 5)
for (let option of datalist.children)
option.value = ''
values.forEach(value => {
const option = document.createElement('option')
option.value = value
datalist.appendChild(option)
})
})
<!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>Document</title>
<script src="./test.js" defer></script>
</head>
<body>
<input type="text" list="datalist" id="input">
<datalist id="datalist"></datalist>
<button type="submit" id="submit-button">Submit</button>
</body>
</html>
Related
I, not so long ago, went ahead and built an html dependent dropdown which pulls it's data from an array in the js. The dependencies worked perfectly fine until I realized that I needed to add a search function to the dropdown.
I went through different alternatives and to me the simplest option was to use select2 plugin. The problem I am having is that when using select2, it doesn't seem to be triggering the EventListener (Line 43 in JS) I had previously setup for the regular select.
Find below what I have attempted:
HTML:
<!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">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/select2#4.0.13/dist/css/select2.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/select2#4.0.13/dist/js/select2.min.js"></script>
<title>Document</title>
</head>
<body>
<select id ="level1" style='width: 300px;'></select>
<select id ="level2" style='width: 300px;'></select>
<select id ="level3" style='width: 300px;'></select>
<hr>
<select id ="level4" disabled></select>
<select id ="level5" disabled></select>
<select id ="level6" disabled></select>
<select id ="level7" disabled></select>
<hr>
<h1 id ="level8"></h1>
<script src="betterdd.js"></script>
</body>
</html>
JS: (Select options are found in var myData = [...])
class DropDown {
constructor(data){
this.data = data;
this.targets = [];
}
filterData(filtersAsArray){
return this.data.filter(r => filtersAsArray.every((item,i) => item === r[i]));
}
getUniqueValues(dataAsArray,index){
const uniqueOptions = new Set();
dataAsArray.forEach(r => uniqueOptions.add(r[index]));
return [...uniqueOptions];
}
populateDropDown(el,listAsArray){
el.innerHTML = "";
listAsArray.forEach(item => {
const option = document.createElement("option");
option.textContent = item;
el.appendChild(option);
});
}
createPopulateDropDownFunction(el,elsDependsOn){
return () => {
const elsDependsOnValues = elsDependsOn.length === 0 ? null : elsDependsOn.map(depEl => depEl.value);
const dataToUse = elsDependsOn.length === 0 ? this.data : this.filterData (elsDependsOnValues);
const listToUse = this.getUniqueValues(dataToUse, elsDependsOn.length);
this.populateDropDown(el,listToUse);
}
}
add(options){
//{target: "level2", dependsOn: ["level1"] }
const el = document.getElementById(options.target);
const elsDependsOn = options.dependsOn.length === 0 ? [] : options.dependsOn.map(id => document.getElementById(id));
const eventFunction = this.createPopulateDropDownFunction (el, elsDependsOn);
const targetObject = { el: el, elsDependsOn: elsDependsOn,func: eventFunction};
targetObject.elsDependsOn.forEach(depEl => depEl.addEventListener("change",eventFunction));
this.targets.push(targetObject);
return this;
}
initialize(){
this.targets.forEach(t => t.func());
return this;
}
eazyDropDown(arrayOfIds){
arrayOfIds.forEach((item,i) =>{
const option = {target: item, dependsOn: arrayOfIds.slice(0,i) }
this.add(option);
});
this.initialize();
return this;
}
}
var dd = new DropDown(myData).eazyDropDown(["level1","level2","level3","level4","level5","level6","level7","level8"])
add the following line inside add method :
const eventFunction = this.createPopulateDropDownFunction (el, elsDependsOn);
el.addEventListener("change", (e) => {
eventFunction();
console.log(e.target.value)
})
and remove the following line:
targetObject.elsDependsOn.forEach(depEl => depEl.addEventListener("change",eventFunction));
<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>TV Show Search</title>
<script src="https://cdn.jsdelivr.net/npm/axios#1.1.2/dist/axios.min.js"></script>
</head>
<body>
<h1>TV Show Search</h1>
<form id="searchForm">
<input type="text" placeholder="TV Show title" name="query">
<button>Search</button>
</form>
<script src="app.js"></script>
</body>
</html>
The result of displayed image in the browser
The below code is searching the picture element data in api and it will display the picture after searching it, My goal is while the first searched picture displayed and when i'm trying to search for the different element the previous displayed picture should be deleted. Right now when i search it prints from where the last picture displayed in the browser.
const form = document.querySelector('#searchForm');
const input = document.querySelector('.name');
form.addEventListener('submit', async function(e) {
e.preventDefault();
const searchTerm = form.elements.query.value;
const config = {params: {q: searchTerm}}
const res = await axios.get(`https://api.tvmaze.com/search/shows`, config)
form.elements.query.value = ''
// console.log(res.data[0].show.image.medium);
// form.elements.query.value = '';
// const img = document.createElement('IMG');
// img.src = res.data[0].show.image.medium;
// document.body.append(img)
makeImages(res.data)
})
const makeImages = (shows) => {
for(let result of shows){
if(result.show.image) {
const img = document.createElement('IMG');
img.src = result.show.image.medium;
document.body.append(img);
}
}
}
You should add a new container for images.
<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>TV Show Search</title>
<script src="https://cdn.jsdelivr.net/npm/axios#1.1.2/dist/axios.min.js"></script>
</head>
<body>
<h1>TV Show Search</h1>
<form id="searchForm">
<input type="text" placeholder="TV Show title" name="query">
<button>Search</button>
</form>
<div id="results-container"></div><!-- new container -->
<script src="app.js"></script>
</body>
</html>
Then the first thing you should do in makeImages is clear all the children inside the container.
const form = document.querySelector('#searchForm');
const input = document.querySelector('.name');
form.addEventListener('submit', async function(e) {
e.preventDefault();
const searchTerm = form.elements.query.value;
const config = {params: {q: searchTerm}}
const res = await axios.get(`https://api.tvmaze.com/search/shows`, config)
form.elements.query.value = ''
// console.log(res.data[0].show.image.medium);
// form.elements.query.value = '';
// const img = document.createElement('IMG');
// img.src = res.data[0].show.image.medium;
// document.body.append(img)
makeImages(res.data)
})
const makeImages = (shows) => {
let container = document.querySelector('#results-container');
container.empty(); // remove all previous images
for(let result of shows){
if(result.show.image) {
const img = document.createElement('IMG');
img.src = result.show.image.medium;
container.append(img); // add images to the container instead of adding them directly to the body.
}
}
}
I'm creating a project in which when I click on the Add button, I add the value entered in the first input to an array. After entering as many values as possible I would like to calculate the numbers in the array and enter them in an array containing the total and show it in the console.
I was able to get the input values to add to the array but can't calculate them afterwards. Can anyone kindly tell me how can I do?
HTML
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="stile.css">
</head>
<body>
<form>
<div>
<input type="number" id="importo">
</div>
<button type="submit" id="aggiungi">AGGIUNGI</button>
<button type="submit" id="calcola">CALCOLA</button>
</form>
<script src="script.js"></script>
</body>
JS
const aggiungi = document.getElementById("aggiungi")
const calcola = document.getElementById("calcola")
const importazione = document.getElementById("importo")
const spese = []
console.log(importazione, motivazione)
aggiungi.onclick = function(e) {
e.preventDefault()
spese.push(Number(importazione.value))
console.log(importazione.value)
console.log(spese)
}
/* calcola.onclick = function (e) {
e.preventDefault()
let somma = 0
let conti = []
for (let i=0; i = spese.length; i++) {
conti = [somma += spese[i]]
console.log(conti)
}
} */
You can use array.reduce:
The reduce() method executes a user-supplied "reducer" callback function on each element of the array, in order, passing in the return value from the calculation on the preceding element. The final result of running the reducer across all elements of the array is a single value.
The first time that the callback is run there is no "return value of the previous calculation". If supplied, an initial value may be used in its place. Otherwise the array element at index 0 is used as the initial value and iteration starts from the next element (index 1 instead of index 0).
const aggiungi = document.getElementById("aggiungi")
const calcola = document.getElementById("calcola")
const importazione = document.getElementById("importo")
const spese = []
console.log(importazione);
aggiungi.addEventListener("click", e => {
spese.push(Number(importazione.value))
console.log(importazione.value)
console.log(spese)
});
calcola.addEventListener("click", () => {
const result = spese.reduce((previousValue, currentValue) => previousValue + currentValue, 0);
console.log(result);
});
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="stile.css">
</head>
<body>
<form>
<div>
<input type="number" id="importo">
</div>
<button type="button" id="aggiungi">AGGIUNGI</button>
<button type="button" id="calcola">CALCOLA</button>
</form>
<script src="script.js"></script>
</body>
</html>
I am writing code that uses data binding to change the innerHTML of an span to the input of the user, but I can't get it to work. What it should do is show the input on the right side of the input field on both the input fields, but it doesn't. Can someone please help me out.
HTML:
<html lang="en-US">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>My Frontend Framework</title>
</style>
</head>
<body>
<div>
<label>Name:</label>
<input type="text" bit-data="name"/>
<span bit-data-binding="name" style="margin-left: 1rem;"></span>
</div>
<div>
<label>Lastname:</label>
<input type="text" bit-data="LastName"/>
<span bit-data-binding="LastName" style="margin-left: 1rem;"></span>
</div>
<script src="frontend-framework.js"></script>
</body>
</html>
Javascript:
const createState = (stateObj) => {
return new Proxy(stateObj, {
set(target, property, value) {
target[property] = value;
render();
return true;
}
});
};
const state = createState({
name: '',
lastName: ''
});
const listeners = document.querySelectorAll('[bit-data]');
listeners.forEach((element) => {
const name = element.dataset.model;
element.addEventListener('keyup', (event) => {
state[name] = element.value;
console.log(state);
});
});
const render = () => {
const bindings = Array.from(document.querySelectorAll('[bit-data-binding]')).map(
e => e.dataset.binding
);
bindings.forEach((binding) => {
document.querySelector(`[bit-data-binding=${binding}]`).innerHTML = state[binding];
document.querySelector(`[bit-data=${binding}]`).value = state[binding];
});
}
https://jsfiddle.net/Mauro0294/g3170whc/4/
I made some changes to the fiddle to get the desired result. The problem was with your logic to refer the elements using the dataset attributes, so I tried to simplify it.
Some notable changes :
Updated the data-bit to use lastName instead of LastName. Made it same as your state.
Used getAttribute to get the value of the data-* properties to correctly get the reference.
I think this is what you're looking for:
const createState = (stateObj) => {
return new Proxy(stateObj, {
set(target, property, value) {
target[property] = value;
render();
return true;
}
});
};
const state = createState({
name: '',
lastName: ''
});
const listeners = document.querySelectorAll('[bit-data]');
listeners.forEach((element) => {
const name = element.getAttribute('bit-data');
console.log('here', element.getAttribute('bit-data'), JSON.stringify(element.dataset))
element.addEventListener('keyup', (event) => {
state[name] = element.value;
console.log(state);
});
});
const render = () => {
const bindings = Array.from(document.querySelectorAll('[bit-data-binding]')).map((e) => {
return e.getAttribute('bit-data-binding');
});
//console.log('bindings:', bindings, document.querySelectorAll('[bit-data-binding]'));
(bindings ?? []).forEach((binding) => {
document.querySelector(`[bit-data-binding=${binding}]`).innerHTML = state[binding];
document.querySelector(`[bit-data=${binding}]`).value = state[binding];
});
}
<html lang="en-US">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>My Frontend Framework</title>
</head>
<body>
<div>
<label>Name:</label>
<input type="text" bit-data="name"/>
<span bit-data-binding="name" style="margin-left: 1rem;"></span>
</div>
<div>
<label>Lastname:</label>
<input type="text" bit-data="lastName"/>
<span bit-data-binding="lastName" style="margin-left: 1rem;"></span>
</div>
</body>
</html>
Your main issue is this part:
const bindings = Array.from(document.querySelectorAll('[bit-data-binding]')).map(
e => e.dataset.binding
);
or more specifically e.dataset.binding. Your elements do not a have data-binding attribute, which would be the prerequisite for using dataset.binding. You can use e.getAttribute('bit-data-binding') instead.
But your logic is also flawed: As it currently stands, entering text into an input is pointless, as the state is never updated.
Finally, note that you spell LastName with a capital L in your DOM but lowercased in your state object.
Here is my HTML and JS code:
<!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>2-d0</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h2>2-D0</h2>
<div id="heading">
<textarea id="text"></textarea>
<button id="button">Add</button>
</div>
<div id="lists">
</div>
<script src="functions.js"></script>
</body>
</html>
Here is my Javascript code
'use strict'
const buttonclick = document.getElementById('button')
const list = document.getElementById('lists')
const a = "<span><button class = 'rbutton'>X</button></span>" //list-item button
const clickhandler = () => {
const text = document.getElementById('text')
//creating a list element
if(text.value != ''){
let Newdiv = document.createElement('div')
// appending elements
Newdiv.innerHTML = text.value + a
list.appendChild(Newdiv)
let b = document.getElementsByClassName('rbutton')
if(b !=[]){
for(let i = 0; i < b.length; i++){
b[i].addEventListener('click', function(){
b[i].parentElement.parentElement.remove();
console.log(b)
})
}
}
//reseting the textarea value
text.value = ''
}
}
buttonclick.addEventListener('click', clickhandler)
An error in shown on delete a item: Cannot read property 'parentElement' of undefined at HTMLButtonElement. .
Can someone please explain what is wrong in my code and what does the error mean.
thankyou
On every click of the button you are attaching event handlers to the whole group again.
On the first iteration, 1st button has one delete handler.
On second iteration, 1st button has 2 event handler(one for buttons[0] and one for buttons[1]), and 2nd has one.
So on.
Use this. It will always point to the element to the event on which the event handler is attached:
this.parentElement.parentElement.remove();
Another way is to simply use this.parentElement.parentElement.remove()
<!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>2-d0</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h2>2-D0</h2>
<div id="heading">
<textarea id="text"></textarea>
<button id="button">Add</button>
</div>
<div id="lists">
</div>
<script>
"use strict";
const buttonclick = document.getElementById('button');
const list = document.getElementById('lists');
const a = "<span><button class = 'rbutton'>X</button></span>"; //list-item button
const clickhandler = () => {
const text = document.getElementById('text');
//creating a list element
if(text.value != '') {
let Newdiv = document.createElement('div');
// appending elements
Newdiv.innerHTML = text.value + a;
list.appendChild(Newdiv);
let b = document.getElementsByClassName('rbutton');
for(let i = 0; i < b.length; i++) {
b[i].addEventListener('click', function() {
this.parentElement.parentElement.remove();
});
}
//reseting the textarea value
text.value = '';
}
}
buttonclick.addEventListener('click', clickhandler);
</script>
</body>
</html>
You should use window.event.target.parentElement... to get the button instead of b[i].parentElement....
"use strict";
const buttonclick = document.getElementById('button');
const list = document.getElementById('lists');
const a = "<span><button class = 'rbutton'>X</button></span>"; //list-item button
const clickhandler = () => {
const text = document.getElementById('text');
//creating a list element
if(text.value != '') {
let Newdiv = document.createElement('div');
// appending elements
Newdiv.innerHTML = text.value + a;
list.appendChild(Newdiv);
let b = document.getElementsByClassName('rbutton');
for(let i = 0; i < b.length; i++) {
b[i].addEventListener('click', function() {
window.event.target.parentElement.parentElement.remove();
});
}
//reseting the textarea value
text.value = '';
}
}
buttonclick.addEventListener('click', clickhandler);
<!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>2-d0</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h2>2-D0</h2>
<div id="heading">
<textarea id="text"></textarea>
<button id="button">Add</button>
</div>
<div id="lists">
</div>
</body>
</html>