im trying to add an eventlistner to this html tag that i am creating with a api call
handleProducts()
function handleProducts() {
var display = document.getElementById("display")
var url = "http://127.0.0.1:8000/api/product/"
fetch(url)
.then((resp) => resp.json())
.then(function (data) {
console.log(data)
var products = data
for (var i in products) {
var product = `
<div class="col-lg-4">
<img class="thumbnail" src="${products[i].img}" alt="">
<div class="box-element product">
<h6><strong>${products[i].title}</strong></h6>
<hr>
<button data-product=${products[i].id} data-action = "add" class="btn btn-outline-secondary add-btn update-cart">Add to Cart</button>
<a class="btn btn-outline-success" href="">View</a>
<h4 class="price">${products[i].price}</h4>
</div>
</div>
`
display.insertAdjacentHTML('beforeend', product)
}
})
}
function handleAddToCart(){
var updateBtns = document.getElementsByClassName("update-cart")
console.log(updateBtns)
for (var y = 0; y < updateBtns.length; y++) {
updateBtns[y].addEventListener("click", function () {
console.log("Clicked")
})
}
}
handleAddToCart()
Ive included all the code because mabye there is something else i need to add when adding an eventlistner to this type of html code. The problem is that this does not console log clicked when i click the button. Any ideas?
You are calling both functions at the same time, and the fetch is not complete yet while you try to add the event listener. You can move the function call handleAddToCart() inside the first function after you create that element.
Related
I'm trying to make a some dynamically generated bootstrap cards with a button clearing the page and showing the info (which I have stored on a Firebase project). I managed to make the cards show properly but I'm blocked and can't find a way to make each button show a different information.
So basically I have this:
Cards
Content showing
It has to be something close to the eventListener or another way to write the code... I think the way I did it is why its showing all the information on the same card, but how can I write the code right? I'm trying for 2 days and can't work around it.
OBS: I know my code can be a little messy or could be best ways to do some stuff but I'm still learning.
import { getWikiTI } from './prevenirWiki.js'
const dataCard = document.getElementById('data');
async function loadData() {
let i = 0;
const data = await getWikiTI()
dataCard.innerHTML = data.map(d => `
<div class="card border-primary mb-3 fb-item" style="max-width: 20rem;">
<div class="card-header">${d.setor}</div>
<div class="card-body">
<h4 class="card-title">${d.descricao}</h4>
<button type="button" class="btn btn-lg btn-primary fb-item" id="carregar-card">Carregar</button>
</div>
</div>
`,
console.log(data),
console.log(i)
).join('')
const btnCarregar = document.getElementById('carregar-card')
btnCarregar.addEventListener('click', () => {
dataCard.innerHTML = data.map(function (d) {
return `${d.conteudo}`;
})
document.body.appendChild(dataCard)
})
}
document.onload = loadData();
EDIT: Alright ! I managed to get it working... not the best code in the world but it's something :) If anyone has any opinions or ideias on how to improve it, im all ears.
import { getWikiTI } from './prevenirWiki.js'
const dataCard = document.getElementById('data');
async function loadData() {
let i = 0;
const data = await getWikiTI()
dataCard.innerHTML = data.map(d => `
<div class="card border-primary mb-3 fb-item" style="max-width: 20rem;">
<div class="card-header">${d.setor}</div>
<div class="card-body">
<h4 class="card-title">${d.descricao}</h4>
<button type="button" class="btn btn-lg btn-primary fb-item" id="carregar-card${i++}">Carregar</button>
<p hidden class="card-text">${d.conteudo}</p>
</div>
</div>
`,
).join('')
for (let i = 0; i <= data.length; i++) {
let btnCarregar = document.getElementById(`carregar-card${i}`)
btnCarregar.addEventListener('click', () => {
dataCard.innerHTML = data[i].conteudo;
document.body.appendChild(dataCard)
})
}
}
document.onload = loadData();
I have a list of user cards. That card contains add and remove button.
I want to remove that card from list of card when I click at remove button.
Code is similar to following:
// function to generate card
function generateUserCard(id) {
return `
<div class="user-card" id="${id}">
<button data-id="${id}" class="add" >Add</button>
<button data-id="${id}" class="remove" >Remove</button>
</div>
`;
}
// function to generate list of user
function generateUsers(users) {
const userGrid = $("#user-grid");
for(let user of users) {
const userCard = generateUserCard(user.id);
userGrid.append(userCard);
// adding event listeners
$(`[data-id=${user.id}]`).on("click", function() {
// I did something like this
(`#${user.id}`).remove(); // But this didn't work
})
}
}
Please help!
There are several issues in the logic used in your click event callback:
The variable id is not accessible in the callback. A quick fix will be to fix the reference so that you are using user.id in the selector instead. Also, you can simply remove it by ID without needing to search for it inside its parent element, since it is unique.
Your selector [data-id]=${user.id} is syntacically incorrect. I suppose you meant [data-id=${user.id}]
You should be using .remove() to remove a node
A quick fix will look like this:
$(`button[data-id=${user.id}].remove`).on("click", function() {
$(`#${user.id}`).remove();
});
See proof-of-concept below:
function generateUserCard(id) {
return `
<div class="user-card" id="${id}">
User ID: ${id}
<button data-id="${id}" class="add" >Add</button>
<button data-id="${id}" class="remove" >Remove</button>
</div>
`;
}
function generateUsers(users) {
const userGrid = $("#user-grid");
for (let user of users) {
const userCard = generateUserCard(user.id);
userGrid.append(userCard);
$(`button[data-id=${user.id}].remove`).on("click", function() {
$(`#${user.id}`).remove();
})
}
}
// For demo only
let i = 0;
$('#btn').on('click', function() {
const userArray = [];
for (let j = 0; j < 3; j++) {
i++;
userArray.push({ id: i });
}
generateUsers(userArray);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="btn">Generate users</button>
<div id="user-grid"></div>
However, an improvement to your code will be to avoid adding new click event listeners to all your newly appended elements. You can simply listen to the click event bubbling up to a parent that is already present at runtime (e.g. #user-grid), and you can bind it outside of your generateUsers function:
$('#user-grid').on('click', 'button.add, button.remove', function() {
const id = $(this).attr('data-id');
$(`#${id}`).remove();
});
See proof-of-concept below:
function generateUserCard(id) {
return `
<div class="user-card" id="${id}">
User ID: ${id}
<button data-id="${id}" class="add" >Add</button>
<button data-id="${id}" class="remove" >Remove</button>
</div>
`;
}
function generateUsers(users) {
const userGrid = $("#user-grid");
for (let user of users) {
const userCard = generateUserCard(user.id);
userGrid.append(userCard);
}
}
// Listen to event bubbling instead!
$('#user-grid').on('click', 'button.remove', function() {
const id = $(this).attr('data-id');
$(`#${id}`).remove();
});
// For demo only
let i = 0;
$('#btn').on('click', function() {
const userArray = [];
for (let j = 0; j < 3; j++) {
i++;
userArray.push({
id: i
});
}
generateUsers(userArray);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="btn">Generate users</button>
<div id="user-grid"></div>
I am trying to dynamically load a bunch of posts from a API and then implement a like button for each of them.
function load_allposts(){
fetch("/posts")
.then(response => response.json())
.then(posts => {
var enc = document.createElement('div');
enc.className = "post-enc";
let s = ``;
posts.forEach(element => {
s += `<div class="p-container">
<div>
<button type="button" class="btn btn-link" class="profile-btn" data-id=${element[0].author_id}> ${element[0].author_name} </button>
</div>
<div class="post-body">
${element[0].body}
</div>
<div class="p1">
<span class="like-status">${element[0].likes}</span> people like this
<button class="like-btn">${element[1]}</button>
</div>
<div class="post-time">
${element[0].timestamp}
</div>
</div>`;
});
enc.innerHTML = s;
document.querySelector('#all-posts').appendChild(enc);
});
}
I would to like to modify the <span class="like-status"> element when I click the <button class="like-btn">. The only way that I can think of to get a reference to <span class="like-status"> is by adding a ID to it by implementing some kind of counter, which I feel is more like a hack rather than real solution.
I tried googling but almost all solutions involved JQuery, which I am not familiar with. Any help would be appreciated.
You can use delegate event binding document.addEventListener('click', function(event) { to trigger click event for dynamically added button.
It will raise click on every element inside document you need to find if it is one which you expect with event.target.matches('button.like-btn').
Then you can find your span with getting parent and then finding span.like-status using querySelector.
Try it below. For demo modified load_allposts. You do not need to do any change in it.
load_allposts();
document.addEventListener('click', function(event) {
if (event.target.matches('button.like-btn')) {
let span = event.target.parentElement.querySelector('span.like-status');
span.innerText = 'Modified';
}
});
function load_allposts() {
let posts = [1]
var enc = document.createElement('div');
enc.className = "post-enc";
let s = ``;
posts.forEach(element => {
s += `<div class="p-container">
<div>
<button type="button" class="btn btn-link" class="profile-btn" data-id=element[0].author_id> element[0].author_name </button>
</div>
<div class="post-body">
element[0].body
</div>
<div class="p1">
<span class="like-status">element[0].likes</span> people like this
<button class="like-btn">element[1]</button>
</div>
<div class="post-time">
element[0].timestamp
</div>
</div>`;
});
enc.innerHTML = s;
document.querySelector('#all-posts').appendChild(enc);
}
<div id='all-posts'>
</div>
Note event delegation have extra overhead so alternatively you can use below code.
Here added two functions added as below and added one line bindClickEvent(enc); at end of load_allposts function.
likeClick - perform custom logic to update span.like-status
bindClickEvent - bind click event to all button.like-btn inside div
Call bindClickEvent(enc); at end of load_allposts function.
Try it below.
load_allposts();
// perform custom logic to update span.like-status
function likeClick(event) {
// querySelector will return first matching element
let span = event.target.parentElement.querySelector('span.like-status');
span.innerText = 'Modified';
}
// bind click event to all button.like-btn inside div
function bindClickEvent(enc) {
// querySelectorAll will return array of all matching elements
let buttons = enc.querySelectorAll('button.like-btn');
// loop over each button and assign click function
for (let i = 0; i < buttons.length; i++) {
buttons[i].onclick = likeClick;
}
}
function load_allposts() {
let posts = [1]
var enc = document.createElement('div');
enc.className = "post-enc";
let s = ``;
posts.forEach(element => {
s += `<div class="p-container">
<div>
<button type="button" class="btn btn-link" class="profile-btn" data-id=element[0].author_id> element[0].author_name </button>
</div>
<div class="post-body">
element[0].body
</div>
<div class="p1">
<span class="like-status">element[0].likes</span> people like this
<button class="like-btn">element[1]</button>
</div>
<div class="post-time">
element[0].timestamp
</div>
</div>`;
});
enc.innerHTML = s;
document.querySelector('#all-posts').appendChild(enc);
// assign click event to buttons inside enc div.
bindClickEvent(enc);
}
<div id='all-posts'>
</div>
I have this code and on Tab click, it is working fine, but I can't change tab from button click.
var tabs = document.getElementById('icetab-container').children;
var tabcontents = document.getElementById('icetab-content').children;
var myFunction = function () {
var tabchange = this.mynum;
for (var int = 0; int < tabcontents.length; int++) {
tabcontents[int].className = ' tabcontent';
tabs[int].className = ' icetab';
}
tabcontents[tabchange].classList.add('tab-active');
this.classList.add('current-tab');
}
for (var index = 0; index < tabs.length; index++) {
tabs[index].mynum = index;
tabs[index].addEventListener('click', myFunction, false);
}
I tried this code :
function changeView() {
tabs[1].click(); // and
tabs[1].addEventListener('click', myFunction, false);
}
What is the right way to do it?
This is the HTML code :
<div class="codepen-container">
<div id="icetab-container">
<div class="icetab current-tab">Add Group <i class="fa fa-users" aria-hidden="true"></i></div>
<div class="icetab">Add User <i class="fa fa-user" aria-hidden="true"></i></div>
</div>
<div class="col-md-offset-8 col-md-4 ">
<button type="button" onclick="changeView()" Text="Save"class="btn btn-default" />
</div>
What you are doing now is adding a new click event listener fot the tab with 1 index, so user still need to click on it to get it working.
You just need to call the myFunction with providing this with setting this.mynum to the index you want to be selected
function changeView() {
myFunction.call({mynum: 1});
}
More readable is:
function changeView(index) {
myFunction.call({mynum: index});
}
and you can call:
changeView(0) for the first tab
changeView(1) for second tab
...
This is my HTML(list of people)
<button class="btn btn-primary btn-block" id="add_user">Add new user</button>
<ul id="users_list" contenteditable="true">
<li class="user_data">
<img src="http://img.dunyanews.tv/blog_user_images/anchors//15_user_icon.png" alt="44user" class="user_image" />
<div class="user_data_id text-left">Id:></div>
<div class="user_data_username text-left">Username:</div>
<div class="user_data_firstname text-left">Firstname:</div>
<div class="user_data_lastname text-left">Lastname:</div>
<button class="btn btn-danger delete_user">X</button>
</li>
</ul>
This is my Javascript (one function for adding and one for deleting). First work well, but second (deleting) - delete only first (or [0]) element
var addUser = document.getElementById('add_user'),
usersList = document.getElementById('users_list'),
userTemplate = document.getElementsByClassName('user_data');
deleteUser = document.getElementsByClassName('delete_user');
//Function for adding elements
addUser.addEventListener('click', (function() {
var newUser = userTemplate[0].cloneNode(true);
usersList.appendChild(newUser);
}));
//Function for deleting element
for (i = 0; i < deleteUser.length; i++) {
(function(e) {
deleteUser[e].addEventListener('click', (function() {
usersList.removeChild(userTemplate[e]);
}));
})(i);
}
Try this code:
I changed the display on the default template to hidden so we will also have one user to clone from. Otherwise, the user can delete all and we have nothing to clone from.
<button class="btn btn-primary btn-block" id="add_user">Add new user</button>
<ul id="users_list" contenteditable="true">
<--! This display none is important -->
<li class="user_data" style="display: none;">
<img src="http://img.dunyanews.tv/blog_user_images/anchors//15_user_icon.png" alt="44user" class="user_image" />
<div class="user_data_id text-left">Id:></div>
<div class="user_data_username text-left">Username:</div>
<div class="user_data_firstname text-left">Firstname:</div>
<div class="user_data_lastname text-left">Lastname:</div>
<button class="btn btn-danger delete_user">X</button>
</li>
</ul>
To make sure we are deleting the correct user, I added an array of names for testing.
var names = ['Noah', 'Sophia', 'Liam', 'Emma', 'Jacob', 'Olivia', 'Mason', 'Isabella', 'William', 'Ava', 'Ethan', 'Mia', 'Michael', 'Emily', 'Alexander', 'Abigail', 'Jayden', 'Madison', 'Daniel', 'Elizabeth'];
var addUser = document.getElementById('add_user'),
usersList = document.getElementById('users_list'),
userTemplate = document.getElementsByClassName('user_data');
deleteUser = document.getElementsByClassName('delete_user');
//Function for adding elements
addUser.addEventListener('click', (function() {
var newUser = userTemplate[0].cloneNode(true);
// add name to user so we can test.
newUser.getElementsByClassName('user_data_firstname')[0].innerHTML += ' ' + names.pop();
// make this user visible
newUser.style.display = '';
// Wire click event to remove this user.
newUser.getElementsByClassName('delete_user')[0].addEventListener('click', (function() {
usersList.removeChild(newUser);
}));
usersList.appendChild(newUser);
}));
// Add one user
addUser.click();
Note: You start with one user and you loop through the user list to add the remove, so when new users are added, the click to remove event is not wired.
I removed the loop and wire it as we add a new user.
Obviously our code will fail if we click on add users and we run out of names, but that's enough for our test.