Update Array when new elements are displayed on webpage - javascript

I'm working on a chrome extension where I need to gather the total number of books in a library.
The code below works fine, however, the page where I'm getting the data only loads half of the books at once until you scroll down further and the array doesn't update accordingly.
Is there a way to automatically update the array to keep up with the changes?
let list = document.querySelectorAll("ul > li");
let numBooks = [];
for (let i = 0; i < list.length; i++) {
numBooks.push(i + 1);
}
console.log(numBooks);

// Variables
let list = document.querySelectorAll("ul > li");
let mlist = document.querySelector("ul");
let numBooks = [];
// Push books to array
for (let i = 0; i < list.length; i++) {
numBooks.push(i + 1);
}
// Observe changes
const observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (mutation.addedNodes.length) {
// Create Element: Book Count
let url = "myurl";
let nBooks = numBooks.length / 10;
let nChanges = mutation.addedNodes.length - 1;
if (url == window.location.href) {
let el = document.createElement("span");
el.innerHTML = nBooks + nChanges;
let par = document.getElementsByTagName("h2")[0];
par.appendChild(el);
}
}
});
});
observer.observe(mlist, {
childList: true,
});

Related

Input File Processing

I have a piece of code that is working....it allows me to display multiple files in a list when a user clicks on Choose File. The code works fine. However, I am trying to figure out if it's possible to append to the list instead of creating a new one each time. I've researched this most of the afternoon and a vast majority of the articles say it's not easily done. Should I be using a FormData approach instead? Would that buy me anything?
Here is my Javascript code...It works fine...
window.onload = function() {
const inputElement = document.getElementById("my_files");
const fileNames = document.getElementById("file_names");
let fileList = [];
function removeFile(event) {
event.preventDefault();
let filename = this.dataset.filename;
let modifiedFileList = new DataTransfer();
for (let i = 0; i < fileList.length; i++) {
if (fileList[i].name !== filename) {
modifiedFileList.items.add(fileList[i]);
}
}
inputElement.files = modifiedFileList.files;
fileList = inputElement.files;
handleFiles(fileList);
return false;
}
inputElement.addEventListener("change", handleFilesListener, false);
function handleFilesListener() {
fileList = this.files;
handleFiles(fileList);
}
function handleFiles(fileList) {
fileNames.textContent = '';
for (let i = 0; i < fileList.length; i++) {
let listElement = document.createElement("li");
let textNode = document.createTextNode(fileList[i].name);
listElement.appendChild(textNode);
listElement.setAttribute("class","attachmentname");
let removeButton = document.createElement("button");
removeButton.innerHTML = "Remove&nbsp";
removeButton.setAttribute('type', 'button')
removeButton.setAttribute("class", "button121");
removeButton.setAttribute('data-filename', fileList[i].name)
removeButton.addEventListener('click', removeFile)
listElement.appendChild(removeButton);
fileNames.appendChild(listElement);
}
}
}
Again, I'm trying to figure out if I can append to this list instead of constantly looping through it if the list changes. I did try to do an append instead of add below...but that didn't work. I'm a self proclaimed newb...so please go easy on me. :).
let filename = this.dataset.filename;
let modifiedFileList = new DataTransfer();
for (let i = 0; i < fileList.length; i++) {
if (fileList[i].name !== filename) {
modifiedFileList.items.add(fileList[i]);
}
}
Thanks in advance for any pointers.

Is there a way to "count" elements that haven't rendered on the webpage?

I'm currently working on a chrome extension and I need to count the total number of books in a library. However, only half are counted in the array due to the other half not rendering until scroll down. I tried "MutationObserver", however, the count only shows once the changes are made (when I scroll down). I need the count to be displayed upon load.
// Variables
let list = document.querySelectorAll("ul > li");
let mlist = document.querySelector("ul");
let numBooks = [];
// Push books to array
for (let i = 0; i < list.length; i++) {
numBooks.push(i + 1);
}
// Observe changes
const observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (mutation.addedNodes.length) {
// Create Element: Book Count
let url = "myurl";
let nBooks = numBooks.length / 10;
let nChanges = mutation.addedNodes.length - 1;
if (url == window.location.href) {
let el = document.createElement("span");
el.innerHTML = nBooks + nChanges;
let par = document.getElementsByTagName("h2")[0];
par.appendChild(el);
}
}
});
});
observer.observe(mlist, {
childList: true,
});

Want to delete the dynamically appended card from object array using splice method but the loop iteration is getting wrong values

I am making a task planner using classes.
These tasks are saved in the form of dynamically appended cards.
I am adding three cards in the array in class CardManager.
When I am selecting a card to delete by pressing a delete button the id is retrieved correctly, but in the last delfunc function which has a for loop, I am getting wrong array length.
So splice is not working.
The problem is in the loop of last function called delfunc().
class Card {
constructor(id, cname, pic, description, assignee, dDate, st) {
this.id = id;
this.cname = cname;
this.pic = pic;
this.description = description;
this.assignee = assignee;
this.dDate = dDate;
this.st = st;
// this.info=info;
}
}
class CardManager {
constructor() {
this.cardArr = [];
this.currentId = 1;
}
addcard(cname, pic, description, assignee, dDate, st) {
const nCard = new Card(this.currentId++, cname, pic, description, assignee, dDate, st); //creates
an instance of class card
this.cardArr.push(nCard);
}
}
const cardDeck = new CardManager(); //create an instance of card manager to access the members
// cardDeck.addcard("laundry","test","testing","Saeed","thursday","to do");
let tname = document.querySelector("#text1"); //accepting user input from form
let tdes = document.querySelector("#des");
let assignee = document.querySelector("#assignee");
let dDate = document.querySelector("#dDate");
let sTatus = document.querySelector("#stAtus");
let addButton = document.querySelector("#addButton");
addButton.onclick = function () {
alert("here i am card deck");
cardDeck.addcard(tname.value, "test", tdes.value, assignee.value, dDate.value, sTatus.value);
$('#myModal').modal('hide');
}
let btn1 = document.querySelector("#btn1");
let buttonCount = 1;
btn1.onclick = function displayListHtml() {
let html = "";
alert(cardDeck.cardArr.length);
for (let i = 0; i < cardDeck.cardArr.length; i++) {
html = `<div class="card">
<h1>${cardDeck.cardArr[i].cname}</h1>
<img src="sample.jpg" alt="Denim Jeans" style="width:100%">
<p>${cardDeck.cardArr[i].description}</p>
<p>${cardDeck.cardArr[i].assignee}</p>
<p>${cardDeck.cardArr[i].dDate}</p>
<p>${cardDeck.cardArr[i].st}</p>
<p>${cardDeck.cardArr[i].id}</p>
<p><button class="delete btn btn-primary" id="dbutton_${cardDeck.cardArr[i].id}">
Delete</button></p>
<p><button class="Edit btn btn-primary" id="ebutton_${cardDeck.cardArr[i].id}">
Edit</button></p>
</div>`;
buttonCount++;
}
const taskcontainer = document.querySelector("#taskcontainer");
const element = document.createRange().createContextualFragment(html);
element.querySelector("button.delete")
.addEventListener("click", delfunc);
element.querySelector("button.Edit")
.addEventListener("click", edifunc);
// element.addEventListener("click",yourClickEventHandler);
taskcontainer.append(element);
}
function delfunc() {
alert("i am in delete function");
const taskElement = event.target.closest(".delete"); //see line 74.
let delIdArr = taskElement.id.split("_"); //spliting the id by underscore. i.e . dbuton_id
let retreiveId = delIdArr[1];
for (let j = 0; j < this.cardDeck.cardArr.length; j++) {
if (retreiveId === j) {
this.cardDeck.cardArr.splice(retreiveId, 1);
}
}
}
Here is minimal version your concern. But looks like splice call doing as expected.
const del = (cardArr, retreiveId) => {
for (let j = 0; j < cardArr.length; j++) {
if (retreiveId === j) {
cardArr.splice(retreiveId, 1);
}
}
};
const cardArr = [2, 3, 4];
// delete the index 1
del(cardArr, 1);
console.log(cardArr);
// Case where delete index out of array index
const a = [1];
del(a, 1)
console.log(a);
function delfunc() {
alert("I am in delete function");
const taskElement = event.target.closest(".delete");//see line 74.
let delIdArr = taskElement.id.split("_");
let retrieveId = delIdArr[1];
var arr=[];
for (let j = 1; j <= cardDeck.cardArr.length; j++ ) {
if (retrieveId == j) {
arr = cardDeck.cardArr.splice(retreiveId, 1);
}

How do I fix weather-info not showing

I've been trying to add weather-info to my website using api but cant get it to work, don't really know what the problem is. The temperature of a preset location, which is all i need to display, just doesn't show up.
I tried getting elementByID from a div in my HTML-code but this didnt work so now I'm using a table and trying to insert it into the table.
function addWeather(JSONdata) {
/* let temperatureNow = document.getElementById("temp");*/
let table1 = document.getElementById("table");
let rowNr = 1;
let today = new Date();
let time = today.getHours();
for (let i = 0; i < JSONdata.length; i++) {
let tempDate = new Date(JSONdata.timeSeries[i].validTime);
let hoursTemp = tempDate.getHours();
let row = table1.insertRow(rowNr);
let cell1 = row.insertCell(0);
if (time == hoursTemp
) {
/*temperatureNow == JSONdata.timeSeries[i].parameters[1].values[0];*/
for (let index = 0; index < JSONdata.timeSeries[i].parameters.length; index++) {
if (JSONdata.timeSeries[i].parameters[index].name == "t") {
console.log(JSONdata.timeSeries[i])
cell1.innerHTML = JSONdata.timeSeries[i].parameters[index].values + '℃';
}
}
}
}
}

Javascript Pagination Issue

I'm trying to make a simple Javascript pagination function, but I'm having this issue where instead of iterating through the array, it keeps adding new list items to the innerhtml.
I have tried creating an element and appending it to the DOM.
I have tried using if/else statements to display the list items I
want.
<body>
<div class='result'></div>
<button class="add">+</button>
<script src='pretty.js'></script>
</body>
let dogs = [
'goldendoodle',
'poodle',
'afghan hound',
'golden retriever',
'labrador',
'chihuahua',
'pitbull',
'german shepherd',
'greyhound',
'bull terrier'
]
let high = 1;
let low = 0;
let result = document.querySelector('.result');
let add = document.querySelector('.add');
function Pagination(low,high) {
for(var i = 0 ; i < dogs.length;i++) {
let answer = document.createElement('div');
answer.classList.add('dogs-dom');
answer.innerHTML = dogs[i];
result.appendChild(answer);
if(i >= low && i < high) {
answer.style.display ='block';
}
if(i < low || i > high) {
answer.style.display ='none';
}
}
}
Pagination(low,high);
add.addEventListener('click', () => {
low += 2;
high += 2;
Pagination(low,high);
});
When I click the button, I want the next two array items to appear and replace the last two shown.
To use the approach you've outlined above you'll need to clear the innerHtml of the result element before appending new children. At the top of your Pagination function try result.innerHtml = '';.
That said if you are using a hide/show approach to paginate the list it would be more efficient to create the dom elements only once and modify the style.display property of each instead of clearing out the result and re-creating all of the answer divs on every click.
Your Pagination function only adds elements to the dom each time it is called.
You can either remove the existing elements every time Pagination is called, and render only those that should be displayed, e.g.:
function Pagination(low,high) {
result.innerHTML = ''; // remove all children of result
// only render the children which should be visible
for(var i = low ; i < high;i++) {
let answer = document.createElement('div');
answer.classList.add('dogs-dom');
answer.innerHTML = dogs[i];
result.appendChild(answer);
}
}
Or you can use display: block; / display: none. (Will not scale very well with large lists)
function Pagination(low,high) {
// only append all dogs once
if(result.childElementCount === 0) {
for(var i = 0; i < dogs.length;i++) {
let answer = document.createElement('div');
answer.classList.add('dogs-dom');
answer.style.display ='none';
answer.innerHTML = dogs[i];
result.appendChild(answer);
}
}
// toggle display: none / block for each element
for(var i = 0; i < dogs.length;i++) {
if(i >= low && i < high)
answer.style.display ='block';
else
answer.style.display ='none';
}
}
As a bonus, heres a reusable pagination class example:
function Pagination(container, items) {
this.container = container;
this.result = container.querySelector('.result');
this.prevBtn = container.querySelector('.prev');
this.nextBtn = container.querySelector('.next');
this.items = items;
this.offset = 0;
this.limit = 5;
this.updateDom();
this.prevBtn.onclick = this.prevPage.bind(this);
this.nextBtn.onclick = this.nextPage.bind(this);
}
Pagination.prototype.nextPage = function() {
if((this.offset + this.limit) < this.items.length)
this.offset += this.limit;
this.updateDom();
};
Pagination.prototype.prevPage = function() {
if(this.offset >= this.limit)
this.offset -= this.limit;
this.updateDom();
};
Pagination.prototype.updateDom = function() {
this.result.innerHTML = '';
let stop = Math.min(this.offset + this.limit, this.items.length);
for(let i = this.offset; i < stop; i++) {
let el = document.createElement("div");
el.appendChild(document.createTextNode(this.items[i]));
this.result.appendChild(el);
}
let hasPrev = this.offset > 0;
if(hasPrev)
this.prevBtn.classList.remove('hide');
else
this.prevBtn.classList.add('hide');
let hasNext = (this.offset + this.limit) < this.items.length;
if(hasNext)
this.nextBtn.classList.remove('hide');
else
this.nextBtn.classList.add('hide');
};
let items = [];
for (let i = 1; i <= 50; i++)
items.push(`Item ${i}`);
let pagination = new Pagination(document.querySelector(".paginate"), items);
// You can also programatically switch to the next / prev page:
// pagination.nextPage();
// pagination.prevPage();
.hide { visibility: hidden; }
<div class="paginate">
<div class="result"></div>
<button class="prev">PREV</button>
<button class="next">NEXT</button>
</div>
Maybe this is along the lines of what you want to do?
It tracks only a globalIndex (which would be like like your 'low' variable).
The showNextTwoItems function:
- Notes the indexes where we should start and end
- Clears the container div
- Enters a while loop that appends items and increments the current index
- Updates the globalIndex when enough items have been added
let dogs = [ 'goldendoodle', 'poodle', 'afghan hound', 'golden retriever', 'labrador', 'chihuahua', 'pitbull', 'german shepherd', 'greyhound', 'bull terrier' ],
containerDiv = document.querySelector('.result'),
addBtn = document.querySelector('.add'),
globalIndex = 0; // Tracks where we left off (starts at zero)
const NUMBER_TO_SHOW = 2;
addBtn.addEventListener("click", showNextTwoItems); // Calls function on click
function showNextTwoItems(){
let numberToShow = NUMBER_TO_SHOW, // In case we ever want to change numberToShow
currentIndex = globalIndex, // Gets local copy of globalIndex (always < dogs.length)
// Lets us stop looping when we've shown enough or reach the end of the array
stopBeforeIndex = Math.min(currentIndex + numberToShow, dogs.length);
containerDiv.innerHTML = ""; // Clears div
while(currentIndex < stopBeforeIndex){
// Creates and appends a text node with the next dog
const newItem = document.createTextNode(dogs[currentIndex]);
containerDiv.appendChild(newItem);
// Creates and appends a line break
const lineBreak = document.createElement("BR");
containerDiv.appendChild(lineBreak);
// Moves on to the next index
currentIndex++;
}
// Updates global index (making sure it is not too big for the array)
globalIndex = currentIndex < dogs.length ? currentIndex : 0;
}
<button class="add">+</button>
<div class='result'></div>

Categories

Resources