I have one button that shows all my products in the webpage, each product is loaded from an array and inserted with innerHTML, each product is inserted with one button, i tried to assing one function for those buttons, but nothing happens:
Show.addEventListener('click', () => {
flag = flag + 1
if (flag == 2) {
document.getElementById('ItemsID').disabled = true
}
console.log(ItemsID.options[ItemsID.selectedIndex].value)
if (ItemsID.options[ItemsID.selectedIndex].value == "Vuelos") {
document.getElementById('ShowItems').disabled = true
VUELOSPROMOCIONES.forEach((VUELOSPROMOCIONES, indice) => {
div_Vuelos.innerHTML += `
<div class="card" id="persona${indice + 1}" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Vuelo Promocional ${indice + 1}</h5>
<p>Origen: ${VUELOSPROMOCIONES.origen}</p>
<p>Destino: ${VUELOSPROMOCIONES.destino}</p>
<p>Fecha Ida: ${VUELOSPROMOCIONES.FechaIda}</p>
<p>Fecha Vuelta: ${VUELOSPROMOCIONES.FechaVuelta}</p>
<p>Precio: ${VUELOSPROMOCIONES.precio}</p>
</div>
</div>
**<button id="Enviar">Click me</button>**
`
})
} else if (ItemsID.options[ItemsID.selectedIndex].value == "Autos") {
PROMOCIONESAUTOS.forEach((PROMOCIONESAUTOS, indice) => {
document.getElementById('ShowItems').disabled = true
div_Autos.innerHTML += `
<div class="card" id="Autos Disponibles ${indice + 1}" style="width: 18rem;">
<div class="card-title">Persona ${indice + 1}</h5>
<p>Modelo: ${PROMOCIONESAUTOS.modelo}</p>
<p>Marca: ${PROMOCIONESAUTOS.marca}</p>
<p>Color: ${PROMOCIONESAUTOS.color}</p>
</div>
</div>
<button id="Enviar">Click me</button>
`
})
}
})
if i insert the same button in the html, it works with the same id
Related
I have made a TODO app and added a counter to keep a count of the items in the list. If the counter hits zero, I've set it to re-show a message 'You currently have no tasks. Use the input field above to start adding.'
if(count === 0){
noTasksText.classList.remove('d-none');
}
In the console I print out the div and it doesn't have d-none in the class list any more which is what I want, however, in the actual DOM it does.
Here is a full example - https://codepen.io/tomdurkin/pen/LYdpXKJ?editors=1111
I really can't seem to work this out. I can't seem to interact with that div when the counter becomes zero, however I can get console logs etc to show when expected.
Any help would be appreciated!
const mainInput = document.querySelector('#main-input');
const todoContainer = document.querySelector('#todo-container');
const errorText = document.querySelector('#js-error');
const noTasksText = document.querySelector('.js-no-tasks')
let tasks = [];
let count = 0;
// focus input on load
window.onload = () => {
mainInput.focus();
const storedTasks = JSON.parse(localStorage.getItem('tasks'));
if (storedTasks != null && storedTasks.length > 0) {
// set count to number of pre-existing items
count = storedTasks.length
// hide the 'no tasks' text
noTasksText.classList.add('d-none');
// overwrite tasks array with stored tasks
tasks = storedTasks;
tasks.forEach(task => {
// Build the markup
const markup = `
<div class="js-single-task single-task border-bottom pt-2 pb-2">
<div class="row">
<div class="col d-flex align-items-center js-single-task-name">
<h5 class="mb-0" data-title="${task}">${task}</h5>
</div>
<div class="col d-flex justify-content-end">
<button class="js-remove-task d-block btn btn-danger">Remove Item</button>
</div>
</div>
</div>`;
// Append it to the container
todoContainer.innerHTML += markup;
});
} else {
if (noTasksText.classList.contains('d-none')) {
noTasksText.classList.remove('d-none');
}
}
};
// event listener for 'enter on input'
mainInput.addEventListener("keydown", e => {
// if error is showing, hide it!
if (!errorText.classList.contains('d-none')) {
errorText.classList.add('d-none');
}
if (e.key === "Enter") {
// Get the value of the input
let inputValue = mainInput.value;
if (inputValue) {
// Build the markup
const markup = `
<div class="js-single-task border-bottom pt-2 pb-2">
<div class="row">
<div class="col d-flex align-items-center js-single-task-name">
<h5 class="mb-0" data-title="${inputValue}">${inputValue}</h5>
</div>
<div class="col d-flex justify-content-end">
<button class="js-remove-task d-block btn btn-danger">Remove Item</button>
</div>
</div>
</div>`;
// hide 'no tasks' text
noTasksText.classList.add('d-none');
// Append it to the container
todoContainer.innerHTML += markup;
// Push value to 'tasks' array
tasks.push(inputValue);
// Put in localStorage
textTasks = JSON.stringify(tasks);
localStorage.setItem("tasks", textTasks);
// Reset the value of the input field
mainInput.value = '';
// add 1 to the count
count++
} else {
// Some very basic validation
errorText.classList.remove('d-none');
}
}
});
// remove task
todoContainer.addEventListener('click', (e) => {
// Find the button in the row that needs removing (bubbling)
const buttonIsDelete = e.target.classList.contains('js-remove-task');
if (buttonIsDelete) {
// Remove the HTML from the screen
e.target.closest('.js-single-task').remove();
// Grab the name of the single task
let taskName = e.target.closest('.js-single-task').querySelector('.js-single-task-name h5').getAttribute('data-title');
// filter out the selected word
tasks = tasks.filter(item => item != taskName);
textTasks = JSON.stringify(tasks);
localStorage.setItem("tasks", textTasks);
// update counter
count--
// check if counter is zero and re-show 'no tasks' text if true
if (count === 0) {
noTasksText.classList.remove('d-none');
console.log(noTasksText);
}
}
});
body {
background: #e1e1e1;
}
<div class="container">
<div class="row d-flex justify-content-center mt-5">
<div class="col-10 col-lg-6">
<div class="card p-3">
<h2>To dos</h2>
<p>
Use this app to keep a list of things you need to do
</p>
<input class="form-control" id="main-input" type="text" placeholder="Type your todo and hit enter..." class="w-100" />
<small id="js-error" class="text-danger d-none">
Please type a value and press enter
</small>
<hr />
<h4 class="mb-5">Your 'To dos'</h4>
<div id="todo-container">
<!-- todos append in here -->
<div class="js-no-tasks">
<small class="d-block w-100 text-center mb-3">
<i>
You currently have no tasks. Use the input field above to start adding
</i>
</small>
</div>
</div>
</div>
<!-- /card -->
</div>
</div>
</div>
Upon setting innerHTML by using += innerHTML the node noTasksText is lost, because browser processes the whole new set innerHTML and creates new objects. You can either retrieve noTasksText again after that, or append nodes using todoContainer.appendChild. I forked your pen and solved it with the latter solution.
https://codepen.io/aghosey/pen/wvmGwWd
You can do the following, it will work (here innerHTML is changing the DOM, so I added an extra function to recalculate elements after DOM is changed due to innerHTML):
var mainInput = document.querySelector("#main-input");
var todoContainer = document.querySelector("#todo-container");
var errorText = document.querySelector("#js-error");
var noTasksText = document.querySelector(".js-no-tasks");
let tasks = [];
let count = 0;
function getAllElements() {
mainInput = document.querySelector("#main-input");
todoContainer = document.querySelector("#todo-container");
errorText = document.querySelector("#js-error");
noTasksText = document.querySelector(".js-no-tasks");
}
// focus input on load
window.onload = () => {
mainInput.focus();
var storedTasks = JSON.parse(localStorage.getItem("tasks"));
if (storedTasks != null && storedTasks.length > 0) {
// set count to number of pre-existing items
count = storedTasks.length;
// hide the 'no tasks' text
noTasksText.classList.add("d-none");
// overwrite tasks array with stored tasks
tasks = storedTasks;
tasks.forEach((task) => {
// Build the markup
const markup = `
<div class="js-single-task single-task border-bottom pt-2 pb-2">
<div class="row">
<div class="col d-flex align-items-center js-single-task-name">
<h5 class="mb-0" data-title="${task}">${task}</h5>
</div>
<div class="col d-flex justify-content-end">
<button class="js-remove-task d-block btn btn-danger">Remove Item</button>
</div>
</div>
</div>`;
// Append it to the container
todoContainer.innerHTML += markup;
getAllElements();
});
} else {
if (noTasksText.classList.contains("d-none")) {
noTasksText.classList.remove("d-none");
}
}
};
// event listener for 'enter on input'
mainInput.addEventListener("keydown", (e) => {
// if error is showing, hide it!
if (!errorText.classList.contains("d-none")) {
errorText.classList.add("d-none");
}
if (e.key === "Enter") {
// Get the value of the input
let inputValue = mainInput.value;
if (inputValue) {
// Build the markup
const markup = `
<div class="js-single-task border-bottom pt-2 pb-2">
<div class="row">
<div class="col d-flex align-items-center js-single-task-name">
<h5 class="mb-0" data-title="${inputValue}">${inputValue}</h5>
</div>
<div class="col d-flex justify-content-end">
<button class="js-remove-task d-block btn btn-danger">Remove Item</button>
</div>
</div>
</div>`;
// hide 'no tasks' text
noTasksText.classList.add("d-none");
// Append it to the container
todoContainer.innerHTML += markup;
getAllElements();
// Push value to 'tasks' array
tasks.push(inputValue);
// Put in localStorage
textTasks = JSON.stringify(tasks);
localStorage.setItem("tasks", textTasks);
// Reset the value of the input field
mainInput.value = "";
// add 1 to the count
count++;
} else {
// Some very basic validation
errorText.classList.remove("d-none");
}
}
});
// remove task
todoContainer.addEventListener("click", (e) => {
// Find the button in the row that needs removing (bubbling)
const buttonIsDelete = e.target.classList.contains("js-remove-task");
if (buttonIsDelete) {
// Remove the HTML from the screen
e.target.closest(".js-single-task").remove();
// Grab the name of the single task
let taskName = e.target
.closest(".js-single-task")
.querySelector(".js-single-task-name h5")
.getAttribute("data-title");
// filter out the selected word
tasks = tasks.filter((item) => item != taskName);
textTasks = JSON.stringify(tasks);
localStorage.setItem("tasks", textTasks);
// update counter
count--;
// check if counter is zero and re-show 'no tasks' text if true
if (count === 0) {
noTasksText.classList.remove("d-none");
console.log(noTasksText);
}
}
});
body {
background: #e1e1e1;
}
<div class="container">
<div class="row d-flex justify-content-center mt-5">
<div class="col-10 col-lg-6">
<div class="card p-3">
<h2>To dos</h2>
<p>
Use this app to keep a list of things you need to do
</p>
<input class="form-control" id="main-input" type="text" placeholder="Type your todo and hit enter..." class="w-100" />
<small id="js-error" class="text-danger d-none">
Please type a value and press enter
</small>
<hr />
<h4 class="mb-5">Your 'To dos'</h4>
<div id="todo-container">
<!-- todos append in here -->
<div class="js-no-tasks">
<small class="d-block w-100 text-center mb-3">
<i>
You currently have no tasks. Use the input field above to start adding
</i>
</small>
</div>
</div>
</div>
<!-- /card -->
</div>
</div>
</div>
I am looping through an array of objects (rendering data coming from an API) via Vanilla JavaScript (DOM). And I want to pass more than one value (Product ID, Product Name .. etc) to a function triggered in the event of a click button. saveProduct(${proInfo}) as it shown below (proInfo is an object).
The problem is I have been able to pass ONLY ONE value to this function. I tried to pass the variable as an object but it didn't work and got the error (index):1 Uncaught SyntaxError: Unexpected end of input
var shopNcounter = 0;
const baseURL = "***";
const idsArray = [];
const divRow = document.querySelector(".row");
const buttonContainer = document.querySelector("#button-container")
//create column div
function createDiv() {
const div = document.createElement("div");
div.classList.add("col-xl-3");
div.classList.add("col-md-6");
div.classList.add("col-sm-12");
return div;
}
async function getData(id) {
shopNcounter ++;
console.log(shopNcounter);
if(shopNcounter > 5) {
getProInfo(id);
return;
} else if (shopNcounter > 4) {
getProducts(id);
return;
}
idsArray.push(id);
console.log(id);
console.log(idsArray);
if(idsArray.length > 10) {
idsArray.splice(0,6);
}
const api = `**shop=${shopNcounter}&id=${id}`;
try {
const response = await fetch(api);
const data = await response.json();
divRow.innerHTML = "";
if (shopNcounter > 1) {
buttonContainer.innerHTML = `
<button class="btn btn-lg btn-warning px-5" onclick="moveBack(${idsArray[idsArray.length-2]})">رجوع</button>
`
} else {
buttonContainer.innerHTML = "";
}
//mapping through data
data.map(item => {
const div = createDiv();
divRow.appendChild(div);
div.innerHTML+=`
<div class="card text-center h-100 mx-auto border-white shadow" style="width: 16rem;">
<img src="${baseURL + item.image}" class="card-img-top mx-auto" alt="...">
<div class="card-body">
<a onclick=handleClick(${item.id}) class="btn btn-outline-dark px-5 mt-4" id="goToItemButton">${item.name}</a>
</div>
</div>
`;
});
} catch(error) {console.log(error);}
}
getData(0);
async function getProducts(id) {
//shop > 4 = 5
console.log("Get Product is being executing");
const apiProducts = `**product?id=${id}`;
try {
const response = await fetch(apiProducts);
const data = await response.json();
console.log(data);
divRow.innerHTML = "";
//mapping through data
data.map((item, index) => {
var proInfo = {
pid: item.PID,
pname: item.PName
}
const div = createDiv();
divRow.appendChild(div);
div.innerHTML+=`
<div class="card text-center h-100 mx-auto border-white shadow" style="width: 16rem;">
<img src="${baseURL + item.image}" class="card-img-top mx-auto" alt="...">
<div class="card-body">
<h5>${item.PName}</h5>
<p class="product-price">Price: ${item.PSelPrice}</p>
<a onclick=saveProduct(${item.PID}) class="btn btn-danger px-5 mt-4"> Add to Card </a>
</div>
</div>
`;
});
} catch(error) {
console.log(error);
}
}
function moveBack(id) {
shopNcounter = shopNcounter -2;
getData(id);
}
function handleClick(id) {
let clickedButton = document.querySelector("#goToItemButton");
clickedButton.onclick = "";
getData(id);
}
function saveProduct(g) {
console.log(g);
}
your code should not result in the error you see
However you can make your life easier using delegation
do this
document.getElementById("container").innerHTML = data.map((item,i) => `<div class="card text-center h-100 mx-auto border-white shadow" style="width: 16rem;">
<img src="${baseURL + item.image}" class="card-img-top mx-auto" alt="...">
<div class="card-body">
<h5>${item.PName}</h5>
<p class="product-price">Price: ${item.PSelPrice}</p>
<a data-id="${i}" class="add btn btn-danger px-5 mt-4"> Add to Card </a>
</div>
</div>`).join("");
Have this
document.getElementById("container").addEventListener("click",function(e) {
const tgt = e.target.closest("a");
if (tgt && tgt.className.contains("add")) {
const item = data[tgt.dataset.id]
// here you can add the item
saveProduct({pid: item.PID,pname: item.PName })
}
})
I'm looking for solutions to display the right movie information in my overlay.
I have a "popup window" that appears when i click on a movie and it is supposed to display movie's informations in it but when I click on a movie, no matter which one it is, it only displays the last movie informations, what Should I do to fix it ?
const movieIntegration =() => {
allMovies.map(movie=> {
movieGallery.innerHTML += `<div class="imgContainer">
<img src="${movie.img}" alt="${movie.name}">
<div class="titleContainer">
<div class="movieTitle"> ${movie.name} </div>
<div class="seemore"> See more </div>
</div>
</div>`
const seemore = document.querySelectorAll(".seemore")
seemore.forEach(elm => {
elm.addEventListener("click",() => {
pageContainer.innerHTML += `<div class="popupContainer">
<div class="popup">
${movie.name}
<div id="likeButton">
<img src="img/like.png">
</div>
<div id="editButton">
<img src="img/edit.png">
</div>
<a href="submit.html">
<div id="addingButton">
<img src="img/add.png">
</div>
</a>
</div>
</div>`
console.log(true)
}, true)
})
})
}
the problem is that you are getting all the .seemore element for each movie and you are editing the content of ALL elements for each movie, so the last movie will overwrite the content for all the previous.
A solution could be something like this:
const movieIntegration = () => {
allMovies.map((movie) => {
movieGallery.innerHTML += `<div class="imgContainer">
<img src="${movie.img}" alt="${movie.name}">
<div class="titleContainer">
<div class="movieTitle"> ${movie.name} </div>
<div class="seemore"> See more </div>
</div>
</div>`
})
const seemore = document.querySelectorAll('.seemore')
seemore.forEach((elm, i) => {
elm.addEventListener(
'click',
() => {
pageContainer.innerHTML += `<div class="popupContainer">
<div class="popup">
${allMovies[i].name}
<div id="likeButton">
<img src="img/like.png">
</div>
<div id="editButton">
<img src="img/edit.png">
</div>
<a href="submit.html">
<div id="addingButton">
<img src="img/add.png">
</div>
</a>
</div>
</div>`
},
true
)
})
}
In this way you are mapping the .seemore elements AFTER you finish the map of allMovies and, for each .seemore element you get the associated movie and write his name inside.
I need to create a card element for each row of the database. The problem in my function is that print only the first element. What i forgot? (the query works)
aggiornaEventi();
function aggiornaEventi() {
fetch("../user_area/query/select_allevents.php").then(onResponse).then(onJson);
}
function onResponse(response) {
return response.json();
}
function onJson(json) {
console.log(json);
if (json.length !== 0) {
const eventi = document.getElementById("eventi"); //inserire l'id
for (const evento of json) {
const card = document.getElementById("eventi").innerHTML = `
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">${evento.titolo}</h5>
<p class="card-text">${evento.descrizione} | ${evento.data}</p>
Vai
</div>
</div>
`;;
eventi.appendChild(card);
}
}
}
It looks like you are not creating a new element for each card, instead reusing the same one over an over.
Try:
// ...
const card = Document.createElement();
card.innerHTML = `
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">${evento.titolo}</h5>
<p class="card-text">${evento.descrizione} | ${evento.data}</p>
Vai
</div>
</div>
`;
eventi.appendChild(card);
// ...
You override the innerHTML of the card on 'evento' element. You should either use
const card = document.getElementById("eventi").innerHTML += `...`
or create a new element on every run with Document.createElement();
I fixed with this, thanks to all
function onJson(json) {
console.log(json);
if (json.length !== 0) {
const eventi = document.getElementById("eventi"); //inserire l'id
for (const evento of json) {
const card = document.createElement("eventi");
card.innerHTML = `
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">${evento.titolo}</h5>
<p class="card-text">${evento.descrizione} | ${evento.data}</p>
Vai
</div>
</div>
`;
eventi.appendChild(card);
}
}
}
I am making angular application with angular dynamic form where i am using ng-select library.
The HTML with select:
<div *ngFor="let question of questions" class="form-row {{question.class}}">
<ng-container *ngIf="question.children">
<div [formArrayName]="question.key" class="w-100">
<div *ngFor="let item of form.get(question.key).controls; let i=index" [formGroupName]="i" class="row mt-1">
<div *ngFor="let item of question.children" class="{{item.class}} align-middle">
<div class="w-100">
<dynamic-form-builder [question]="item" [index]="i" [form]="form.get(question.key).at(i)"></dynamic-form-builder>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-6 col-sm-12 col-lg-6 col-md-6">
<div class="form-label-group" *ngIf="showTemplateDropdown">
<ngi-select placeholder="Select Template" [required]="true" [hideSelected]="false" [multiple]="true" [items]="templateList"
dropdownPosition="down" bindLabel="name" bindValue="id" (add)="getTemplateValues($event)" (remove)="onRemove($event)">
</ngi-select>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-6 col-sm-12 col-lg-6 col-md-6">
</div>
<div class="col-6 col-sm-12 col-lg-6 col-md-6 text-right">
<div class="btn-group float-right">
<button class="btn btn-primary btn-round btn-fab mat-raised-button" mat-min-fab="" mat-raised-button="" type="button"
(click)="addControls('template_properties')">
<span class="mat-button-wrapper"><i class="material-icons mt-2">add</i></span>
<div class="mat-button-ripple mat-ripple" matripple=""></div>
<div class="mat-button-focus-overlay"></div>
</button>
<button class="btn btn-primary btn-round btn-fab mat-raised-button" mat-min-fab="" mat-raised-button="" type="button"
(click)="removeControls('template_properties')">
<span class="mat-button-wrapper"><i class="material-icons mt-2">remove</i></span>
<div class="mat-button-ripple mat-ripple" matripple=""></div>
<div class="mat-button-focus-overlay"></div>
</button>
</div>
</div>
</div>
</div>
</div>
</ng-container>
<ng-container *ngIf="!question.children">
<div class="w-100">
<dynamic-form-builder [question]="question" [form]="form"></dynamic-form-builder>
</div>
</ng-container>
</div>
Here the [items]="templateList" has the following,
[{"id":"5bebba2c20ccc52871509d56","name":"Template One"},
{"id":"5bebba5720ccc52871509d57","name":"Template Two"},
{"id":"5bebba8d20ccc52871509d5d","name":"Template Three"}]
I am having (change)="getTemplateValues($event)" event for detecting each change happen when we select an item from dropdown.
The change event function Edited,
getTemplateValues(e) {
this.dynamicFormService.getRest("url" + '/' + e.id").subscribe(res => {
try {
if (res.status === "true") {
res.data.template_properties.forEach(element => {
this.templateArray.push(element);
});
this.form = this.qcs.toFormGroup(this.questions);
for (let i = 0; i < this.templateArray.length; i++) {
this.addControls('template_properties');
}
let propertiesArray = [];
this.templateArray.forEach(element => {
propertiesArray.push(element);
});
this.form.patchValue({
'template_properties': propertiesArray
});
} else {
}
}
catch (error) {
}
})
}
console.log(this.propertiesArray) gives the following,
[{"property_name":"Property one","property_type":4,"property_required":true,"property_origin":1},{"property_name":"Property one","property_type":5,"property_required":true,"property_origin":1}]
In the below image i have deleted template three but the template three properties still showing in it..
Here first i am filtering the data first and ignoring the duplicates and each time i am sending the newly selected values alone to the service and fetching the data related to the id element.id.
And using this.addControls('template_properties') to make open the number of rows, and elements will get patched to the form template_properties.
this.form.patchValue({
'template_properties': propertiesArray
});
As of now everything working fine..
The problem actually arise from here:
If we delete a selected list from dropdown, (say i have selected all three template and i have deleted the template two then that particular template's template_properties needs to get deleted..
I have tried with (remove)="onRemove($event)" but its not working because while remove data, the (change) function also calls..
How can i remove the template_properties with this.removeControls('template_properties'); of particular deleted template name in the change event or remove event..
Remove Function:
onRemove(e) {
console.log(e);
this.dynamicFormService.getRest("url" + '/' + e.value.id").subscribe(res => {
try {
if (res.status === "true") {
for (let i = 0; i < res.data.template_properties.length; i++) {
this.removeControls('template_properties');
}
} else {
}
}
catch (error) {
}
})
}
Remove Control:
removeControls(control: string) {
let array = this.form.get(control) as FormArray;
console.log(array)
array.removeAt(array.length);
}
console.log(array) gives,
It should be pretty easy fix. Use (add) instead of (change) in ngi-select.
onRemove(e) {
console.log(e);
this.dynamicFormService.getRest("url" + '/' + e.value.id").subscribe(res => {
try {
if (res.status === "true") {
this.form = this.qcs.toFormGroup(this.questions);
// Issue is here, you should remove only specific record
// which is being passed from function `e`
for (let i = 0; i < res.data.template_properties.length; i++) {
this.removeControls('template_properties');
}
let propertiesArray = [];
this.templateArray.forEach(element => {
propertiesArray.push(element);
});
this.form.patchValue({
'template_properties': propertiesArray
});
} else {
}
}
catch (error) {
}
})
}
Pass the index in removeControls where you want to remove the element from.
removeControls(control: string, index:number) {
let array = this.form.get(control) as FormArray;
console.log(array)
array.removeAt(index);
}
console.log(array) gives,