cannot add bootstrap popover from javascript to dynamically created elements - javascript

I am using javascript to generate an array of cards in HTML dynamically and currently, I am putting in each card button when the user clicks on this button he should be able to see the popover, but before that, I fetch some data to put on the cards, but something is wrong or missing, the popover doesn't appear, and if I commented the first line of loadPhotos() it works correctly.
$(function () {
$('[data-toggle="popover"]').popover()
})
const getAllPhotos = async () => {
let res = await fetch('https://jsonplaceholder.typicode.com/photos').then(
response => { return response.json() }
)
return res;
}
const displayPhotos = (photos) => {
const view = document.getElementById('photosGrid');
photos.forEach(element => {
view.append(element)
});
}
const createCard = (data) => {
const card = document.createElement('div');
card.classList.add('card', 'col-lg-2', 'm-2', 'p-0')
const optionsButton = document.createElement('button')
optionsButton.innerText = "test"
optionsButton.title = "title of me"
optionsButton.setAttribute('data-toggle', 'popover')
optionsButton.setAttribute('data-content', 'content of me')
card.appendChild(optionsButton)
return card;
}
const createCardsArray = async (data) => {
const res = await data.map(d => {
return createCard(d);
})
return res;
}
const loadPhotos = async () => {
const photos = await getAllPhotos();
const cards = await createCardsArray([1, 2]);
displayPhotos(cards);
}
loadPhotos();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<title>Pinterest Homepage</title>
</head>
<body>
<div class="container-fluid">
<div class="row" id="photosGrid">
</div>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous"></script>
<script type="text/javascript" src="index.js"></script>
</body>
</html>

Initialize the popover after creating the elements, if more elements are added later, you can reinitialize the popover.
JQuery is adding the popover to the elements during initialization, thus elements created after initialization are not assigned their popups by JQuery

Related

Table won't load

I can see the array in the console but it doesnt show up on the table i've tried alot of things and it just hasnt worked out and i've been trying to fix this for like 2 days
i think tobodyHtml doesnt get defined but i dont know how to fix it
document.addEventListener("DOMContentLoaded", function(event) {
function loadUsers(){
var userTbody=document.getElementById('user-tbody');
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200){
var userJSON=this.responseText;
var usersArray=JSON.parse(userJSON);
console.log(usersArray);
var tobodyHtml="";
for(let index = 0; index < usersArray.lenght; index++) {
const user = usersArray[index];
tobodyHtml+="<tr><td>"+user.id+"</td><td>"+user.username+"</td><td>"+user.password+"</td><td>"+user.birthdate+"</td></tr>";
console.log(tobodyHtml);
}
userTbody.innerHTML=tobodyHtml;
}
};
xhttp.open("GET", "http://localhost:8279/users", true);
xhttp.send();
}
loadUsers();
});
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="css/trgames.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.5/umd/popper.min.js" integrity="sha512-8cU710tp3iH9RniUh6fq5zJsGnjLzOWLWdZqBMLtqaoZUA6AWIE34lwMB3ipUNiTBP5jEZKY95SfbNnQ8cCKvA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<table id="user-table">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Password</th>
<th>Birthday</th>
</tr>
</thead>
<tbody id="user-tbody">
</tbody>
</table>
<script src="js/trgames.js"></script>
</body>
</html>
I've had a similar issue before and I find that when creating HTML elements in JavaScript, it's best to use dom document.createElement to create the elements then add the inner text. Then you can append the final data into the body. Here's a simplified version of your code without the HTTP request since I don't have that data handy. There were also a few minor typos in your for loop (length) and some other issues that I cleaned up.
// grab refs to our html elements
const userTbody = document.getElementById("user-tbody");
const tr = document.createElement("tr");
// test data
const usersArray = [
{
id: 23,
username: "Joe",
password: "123",
birthdate: "01/01/2018"
}
];
// event on dom content loaded
document.addEventListener("DOMContentLoaded", function(event) {
// loop through data of users
usersArray.forEach((user) => {
// simple pattern to create the td and place the data on it
const userIdData = document.createElement("td")
userIdData.innerText = user.id
const userNameData = document.createElement("td")
userNameData.innerText = user.username;
const userPasswordData = document.createElement("td")
userPasswordData.innerText = user.password
const userBirthdateData = document.createElement("td")
userBirthdateData.innerText = user.birthdate
// append all of the data we created to the tbody
userTbody.append(
userIdData,
userNameData,
userPasswordData,
userBirthdateData
);
})
})
Here's a codepen example

POST request not posting the value of one property Javascript/jQuery

I am playing with jQuery and Javascript. Working on a TODOs app using li items and with this API: https://jsonplaceholder.typicode.com/todos. I receive 200 items from this API.
I am trying to post a new item created with a click from the button (btn-add) and everything works as expected, with the exception that the post request is leaving in blank one property which is "title". 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">
<link href="/CSS/styles.css" rel="stylesheet">
<title>TO DO List</title>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="/JS/index.js"></script>
</head>
<body>
<div id="inputDIV">
<input id="input" type="text" placeholder="Enter new item">
</div>
<div id="buttons">
<button id="btn-add">Add List Item</button>
<button id="btn-update">Update First Item</button>
<button id="btn-delete">Delete First Item</button>
</div>
<div id="ulDIV">
<ul id="list">
<!-- Here we will insert the list items via JS-->
</ul>
</div>
</body>
</html>
$(document).ready(function(){
let inputNew = $('#input');
let list = $('#list');
let currentInputValue = "";
$('#btn-add').click(createTODOItemAtBackend);
inputNew.on({
"input": function(e){
console.log(e.target.value);
currentInputValue = e.target.value;
},
"keyup": function(e){
if(e.keyCode === 13){
createTODOItemAtBackend();
}
}
})
getTODOListFromBackend();
function clearInputData(){
inputNew.val("");
currentInputValue = "";
}
function createTODODynamically(id, title){
let newListElement = document.createElement("li");
let textNode = document.createTextNode(title);
newListElement.appendChild(textNode);
newListElement.id = id;
return newListElement;
}
function getTODOListFromBackend(){
$.get("https://jsonplaceholder.typicode.com/todos", function(data, status){
let response = data;
for(let i=0; i < response.length; i++){
list.append(createTODODynamically(response[i].id, response[i].title));
}
});
}
let obj = {
"userId": 1,
"title": currentInputValue,
"completed": false
};
function createTODOItemAtBackend(){
$.post("https://jsonplaceholder.typicode.com/todos", obj, function(data, status){
let response = data;
list.append(createTODODynamically(response.id, currentInputValue));
console.log("Item Added to the list!");
clearInputData();
});
}
})
And this is what I see when I read the post information in the web browser:
{userId: "1", title: "", completed: "false", id: 201}
completed: "false"
id: 201
title: ""
userId: "1"
Can somebody help me, why is the property "title" being posted as empty? Thanks in advance
The answer is in what #epascarello hinted on the OP's comment. You set currentInputValue when the input value is changed but there's no code which updates this value to obj.
"input": function(e){
console.log(e.target.value);
currentInputValue = e.target.value;
//Add this line
obj.title = e.target.value;
},
Additional note: You really don't need currentInputValue if you refactor your code, using obj should do the job.

How to make script execute once the first script is loaded

I'm trying to make a web framework and one feature will be a key-value state management tool. I need the second <script> tag to only run after ./script.js loads in.
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./framework.js"></script>
</head>
<body>
<p f-text="name"></p>
<script>
Framework.store('name', 'Joe');
</script>
</body>
</html>
framework.js:
document.querySelectorAll('*').forEach((element) => {
if (element.hasAttribute('f-text')) {
const textValue = element.getAttribute('f-text');
const key = window.fStore.find((x) => x.key === textValue);
element.innerHTML = key.value;
}
});
window.Framework = {
store: (key, value?) => {
if (!value) {
const foundKey = window.fStore.find((x) => x.key === key);
return foundKey.value;
}
window.fStore = [...window.fStore, { key: key, value: value }];
}
}
Error:
SyntaxError: Unexpected token ')'
at /framework.js:12:22
ReferenceError: Framework is not defined
at /:12:5
You need to wait that your script is loaded, you can use this
window.addEventListener('load', function() {
Framework.store('name', 'Joe');
})

Javascript callback called twice

I'm pretty new with coding, and this is really stumping me...
Here is my index.html file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css">
<link rel="stylesheet" href="owlcarousel/owl.carousel.css">
<link rel="stylesheet" href="owlcarousel/owl.theme.default.css">
</head>
<body>
<div class="owl-carousel owl-theme">
</div>
<script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous" type="text/javascript" language="JavaScript"></script>
<script src="jquery.min.js"></script>
<script src="owlcarousel/owl.carousel.js"></script>
<script src="app.js"></script>
<script>
fetch('https://www.paulschlatter.com/slideshow/slideshows.txt')
.then((response) => response.text().then(yourCallback));
let cache = {}
function yourCallback(retrievedText, callback) {
if (cache[retrievedText]) {
console.log('oops')
} else {
let array = []
console.log(callback)
array = retrievedText.split(/\n|\r/g)
let httpsArray = []
let keysArray = []
let mappedArray = array.map(item => {
if (item.substring(0, 5) === 'https') {
httpsArray.push(item)
} else if (item.substring(0, 3) === '202') {
keysArray.push(item)
}
})
const object = { ...keysArray
}
for (const prop in object) {
window['value' + prop] = []
httpsArray.filter(item => item.includes(object[prop])).map(item => {
window['value' + prop].push(item)
})
}
const owlImages = document.querySelector('.owl-carousel'),
owlDiv = document.createElement('img');
owlDiv.setAttribute('src', window.value0.pop())
owlDiv.setAttribute('alt', '')
owlImages.appendChild(owlDiv)
}
}
</script>
</body>
</html>
I am not using npm or anything, just straight JavaScript, and HTML.
The function yourCallback is firing twice, so even when I only console.log hello world it returns hello world twice to my browser.
Obviously this is not ideal, and I believe that the problem lies in the
fetch('https://www.paulschlatter.com/slideshow/slideshows.txt')
.then((response) => response.text().ten(yourCallback));
This was a silly mistake, in my app.js file I had the same fetch and yourCallback function, so it was firing twice cause I called it twice :)

Functions and Web-scraping with Puppeteer

The goal is to launch the html page, enter a url from booking.com, click the button, and have the scraped hotel name, rating, etc returned in the console.
So far, it does not return anything when clicking the button.
It works when the URL is hard-coded, but It says "main is declared but value is never read" in this form. Am i calling the function incorrectly? I'm still new to puppeteer, perhaps I'm overlooking something?
Here is app.js
function main()
{
var Url = document.getElementById('inputUrl').value
const puppeteer = require('puppeteer');
let bookingUrl = Url;
(async () => {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.goto(bookingUrl);
// get hotel details
let hotelData = await page.evaluate(() => {
let hotels = [];
// get the hotel elements
let hotelsElms = document.querySelectorAll('div.sr_property_block[data-hotelid]');
// get the hotel data
hotelsElms.forEach((hotelelement) => {
let hotelJson = {};
try {
hotelJson.name = hotelelement.querySelector('span.sr-hotel__name').innerText;
hotelJson.reviews = hotelelement.querySelector('div.bui-review-score__text').innerText;
hotelJson.rating = hotelelement.querySelector('div.bui-review-score__badge').innerText;
if(hotelelement.querySelector('div.bui-price-display__value.prco-inline-block-maker-helper'))
{
hotelJson.price = hotelelement.querySelector('div.bui-price-display__value.prco-inline-block-maker-helper').innerText;
}
hotelJson.imgUrl = hotelelement.querySelector('img.hotel_image').attributes.src.textContent;
}
catch (exception){
}
hotels.push(hotelJson);
});
return hotels;
});
console.dir(hotelData);
})();
}
Here is index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="app.js" type="text/javascript"></script>
<title></title>
<meta name="description" content="">
<link rel="stylesheet" href="">
</head>
<input id = "inputUrl" type="text" placeholder = "type url here"/>
<button id = "button" button onclick="main();"> click</button>
<body>
<script src="" async defer></script>
</body>
</html>
You could add this before the evaluate:
await page.waitForSelector('div.sr_property_block[data-hotelid]');

Categories

Resources