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();
Related
I tried to make this function getWeekly() run by default when the site first loads but it only runs this part of the code:
dailyBtn.classList.add("active");
weeklyBtn.classList.remove("active");
monthlyBtn.classList.remove("active");
but not the loop under. But it'll show data when I click on the tags. Any ideas? Thanks.
Git link: https://github.com/thusmiley/time-tracking-dashboard.git
Live site link: https://thusmiley.github.io/time-tracking-dashboard
index.html
<div class="report-bottom">
Daily
Weekly
Monthly
</div>
</div>
<div class="stat-wrapper">
<div class="work-bg bg"></div>
<div class="stat" id="work">
<div class="category">
<h2>Work</h2>
<img src="./images/icon-ellipsis.svg" alt="" />
</div>
<div class="card">
<h3 class="work-current"></h3>
<p class="work-previous"></p>
</div>
</div>
</div>
script.js
let Data = [];
fetch("./data.json")
.then((response) => response.json())
.then((data) => Data.push(...data));
let card = document.querySelectorAll(".card");
let dailyBtn = document.getElementById("daily");
let weeklyBtn = document.getElementById("weekly");
let monthlyBtn = document.getElementById("monthly");
function getDaily() {... }
function getWeekly() {
dailyBtn.classList.remove("active");
weeklyBtn.classList.add("active");
monthlyBtn.classList.remove("active");
for (let i = 0; i < Data.length; i++) {
let splitTitle = Data[i].title.split("");
splitTitle = splitTitle.filter((e) => String(e).trim());
let joinTitle = splitTitle.join("");
let current = document.querySelector(`.${joinTitle.toLowerCase()}-current`);
let previous = document.querySelector(
`.${joinTitle.toLowerCase()}-previous`
);
current.innerHTML = `${Data[i].timeframes.weekly.current + "hrs"}`;
previous.innerHTML = `${
"Last Week - " + Data[i].timeframes.weekly.previous + "hrs"
}`;
}
}
function getMonthly() {... }
window.onload = getWeekly();
The very first time that you load the page Data.length is equal to 0, and that's why the loop doesn't iterate. You are using an asynchronous call to load Data, and when getWeekly() is called for the first time, Data is not ready with the info yet (and it only works after when its ready).
You should wait until Data is completely load first, you can try a callback function or even try $.when() using jquery.
I am working on a Vanilla JavaScript Library app with local storage. But, when the page is refreshed even though the data persists in localStorage, but it disappears from the display. When clicking the add book button the data is stored on localStorage, and it persists even after the page is refreshed, but the book cards on display, disappear. I can't find out what is wrong with the code.
Github Address: https://github.com/bdarab/online-library
static addBookToLibrary() {
// addBtn.addEventListener("click", (e) => {
// e.preventDefault();
const displayContainer = document.querySelector("#display");
const displayCard = document.createElement("div");
displayCard.classList.add("card");
displayCard.innerHTML += `
<div class="card-body">
<h5 class="card-title">title:</h5>
<h5 class="card-author">author:</h5>
<h5 class="card-pages">pages: </h5>
</div>
<div class="isRead">
<button type="button" class="btn btn-secondary" id="isRead">
Is it Read?
</button>
<button type="button" class="btn btn-danger" id="delete">
Delete
</button>
</div>
`;
displayContainer.appendChild(displayCard);
} // });
}
// localStorage setup
/* 3. Store Class */
class Store {
static getBooks() {
let books;
if (localStorage.getItem("books") === null) {
books = [];
} else {
books = JSON.parse(localStorage.getItem("books"));
}
return books;
}
static addBook(book) {
const books = Store.getBooks();
books.push(book);
localStorage.setItem("books", JSON.stringify(books));
}
static removeBook(title) {
const books = Store.getBooks();
books.forEach((book, index) => {
if (book.title === title) {
books.splice(index, 1);
}
});
localStorage.setItem("books", JSON.stringify(books));
}
}
I just tried correcting what I didn't think was correct, and it worked well
demo jsfiddle : https://jsfiddle.net/uv7qnt0x/
// missing parameter => boook and
// data has not been displayed on the list => ${book.title} ...etc
static addBookToLibrary(book) {
// addBtn.addEventListener("click", (e) => {
// e.preventDefault();
const displayContainer = document.querySelector("#display");
const displayCard = document.createElement("div");
displayCard.classList.add("card");
displayCard.innerHTML = `
<div class="card-body">
<h5 class="card-title">title: ${book.title}</h5>
<h5 class="card-author">author: ${book.author}</h5>
<h5 class="card-pages">pages: ${book.pageNum}</h5>
</div>
<div class="isRead">
<button type="button" class="btn btn-secondary" id="isRead">
Is it Read?
</button>
<button type="button" class="btn btn-danger" id="delete">
Delete
</button>
</div>
`;
displayContainer.appendChild(displayCard);
} // });
}
/* 4. Event: Display Books */
// document.addEventListener("DOMContentLoaded", DisplayBooks.displayBooks);
window.onload = function(e){
DisplayBooks.displayBook()
}
I'm trying to make an infinite scroll (without jQuery) to show more results in a page. I'm using an IntersectionObserver to detect a div called #paginate and everytime it enters the screen, the #result div will be refreshed.
var result = document.querySelector('#result');
var paginate = document.querySelector('#paginate');
var observer = new IntersectionObserver(entries => {
if (entries.some(entry => entry.isIntersecting))
{
var pagination = 10;
fetch('/kernel/search.php?pagination='+pagination)
.then((response) => {
return response.text();
})
.then((html) => {
result.innerHTML = html;
});
}
});
observer.observe(paginate);
Here's the full code view with HTML:
<html>
<body>
<div class="row justify-content-sm-center justify-content-md-center justify-content-lg-center justify-content-xl-start no-gutters min-vw-100" id="result">
<div class="col-sm-11 col-md-11 col-lg-9-result col-xl-4-result order-0">
<div class="card mx-3 mt-3">
<div class="card-body">
<a class="text-decoration-none" href="?topic=result-1">
<h5 class="card-title">
Result 1
</h5>
</a>
<p class="card-text text-truncate">
Result 1 description.</p>
</div>
</div>
<div class="card mx-3 mt-3">
<div class="card-body">
<a class="text-decoration-none" href="?topic=result-2">
<h5 class="card-title">
Result 2
</h5>
</a>
<p class="card-text text-truncate">
Result 2 description.</p>
</div>
</div>
<div class="alert alert-light text-dark text-center border mx-3 my-3" id="paginate">
More results
</div>
</div>
</div>
<script>
var result = document.querySelector('#result');
var paginate = document.querySelector('#paginate');
var observer = new IntersectionObserver(entries => {
if (entries.some(entry => entry.isIntersecting))
{
var pagination = 10;
fetch('/kernel/search.php?pagination='+pagination)
.then((response) => {
return response.text();
})
.then((html) => {
result.innerHTML = html;
});
}
});
observer.observe(paginate);
</script>
</body>
</html>
It works, but it only works the first time and it doesn't refresh the #result div thereafter. I can see the fetch working in Web Browser > Inspect > Network tab, but there's no activity after the first refresh of the #result div meaning it doesn't detect the #paginate div anymore.
What's going on here? I assume it's because that I'm using an innerHTML and the observer somehow can't detect the #paginate div after the first refresh of the #result div. How can I solve this?
I did it with jQuery and .scroll function and used ajax like this, maybe my code can help you and adapt it to your needs.
$('#customersList').scroll(function () {
if ($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight - 5000) {
// Do your stuff here.
getCustomers();
}
})
function getCustomers(){
let $customerList = $('#customersList');
let offset = ($customerList.attr('data-offset')) ?
$customerList.attr('data-offset') : 0;
if ($customerList.data('requestRunning')) {
return;
}
$customerList.data('requestRunning', true);
return $.ajax({
type: "GET",
data: {offset: offset},
url : routes.routes.customers.get
})
.done(function (data) {
let _htmlCustomersList = ($customerList.is(':empty')) ? '' : $customerList.html();
let response = data.data;
if (response) {
for (const i in response) {
let v = JSON.parse(response[i]);
_htmlCustomersList += '<div class="client-group edit" data-id="' + v['id'] + '"></div><hr>';
}
offset = parseInt(offset) + 200;
$customerList.attr('data-offset', offset).html(_htmlCustomersList);
}
})
.always(function () {
$customerList.data('requestRunning', false);
});
}
my getCustomer function runs before reaching the end of the page and loads 200 more items each time.
I hope this can help you a little bit
It seems you are removing #paginate after the first update, because it is in the #result.
Please, use indentation :)
The #result div is the main wrapper of the content
using innerHTML to update the contents of the div, will result in replacing the entire content inside of the div...
Beyond the fact that innerHTML is absolute, and erases any DOM objects (and their events) hence bad practice, it's not that good solution either, since you'd like to append rather then replace the data, upon scrolling
I would suggest to add a div above the paginate, which will hold the added data, something like:
...
<div id="result"></div>
<div class="alert alert-light text-dark text-center border mx-3 my-3" id="paginate">
More results
</div>
Then use some sort of appending, for the content added
so something like:
.then((html) => {
let res = new DOMParser().parseFromString(html, "text/xml");
document.getElementById("result").appendChild(res);
});
Hope that helps
I have removed innerHTML and replaced it with insertAdjacentHTML.
Because innerHTML seems to reset/forget #paginate after the first refresh of the #result, so the observer can't detect #paginate anymore to trigger the next #result refresh.
I could have used innerHTML with appendChild to do the same but appendChild adds a new div on each #result refresh, which I didn't really like.
As the solution, I'm inserting the refreshed html data before the beginning of #paginate without resetting/forgetting it's element id that's required for the observer to trigger the next #result refresh.
.then((html) => {
paginate.insertAdjacentHTML('beforebegin', html);
});
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>
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.