App demo https://scannerbug.netlify.app/
This is a simple web app that checks the scanned barcode against an API and the data returned gets added to the booklist.
The problem is that at the first time I scan a book, only one gets added, the second time two books get added (this makes it three) and the third time three books get added (this makes it six books in the list). This way I get 6 books in the list when I only scanned three.
Can anyone help me? I will provide any information needed if you can't understand something in my code.
Pastebin JS Code: https://pastebin.com/dj7Pi1JE
//SWIPER SETTING
document.addEventListener("DOMContentLoaded", () => {
const swiper = new Swiper(".swiper-container", {
effect: "coverflow",
grabCursor: true,
centeredSlides: true,
slidesPerView: "auto",
coverflowEffect: {
rotate: 60,
stretch: 0,
depth: 150,
modifier: 1,
slideShadows: true,
},
});
});
//BOOK CLASS
class Book {
constructor(url, cover, title, author, pages, isbn) {
this.url = url;
this.cover = cover;
this.title = title;
this.author = author;
this.pages = pages;
this.isbn = isbn;
}
}
//UI CLASS
class UI {
static displayBooks() {
// let swiper = document.querySelector(".swiper-container").swiper;
// swiper.removeAllSlides();
const storedBooks = [
{
url: "www.google.com",
title: "Test Title",
author: "Test Author",
pages: "230",
isbn: "9778621466124",
},
{
url: "www.isbndb.com",
title: "Test Title",
author: "Test Author",
pages: "100",
isbn: "9778621466124",
},
];
const books = storedBooks;
books.forEach((book) => UI.addBookToList(book));
}
static addBookToList(book) {
//INIT SWIPER ONLY AFTER ADDING BOOKS
let swiper = document.querySelector(".swiper-container").swiper;
swiper.appendSlide(`
<div class="swiper-slide">
<div class="book-cover">
<img src="${book.cover}" alt="" />
</div>
<div class="book-info">
<h2 class="book-title">${book.title}</h2>
<h3 class="book-author">By ${book.author}</h3>
<p class="book-pages">Pages: ${book.pages}</p>
<p>ISBN: ${book.isbn}</p>
</div>
<div class="cta-buttons">
<a href="${book.url}" class="read-more btn" target="_blank">Read More</a>
<a href="#" class="delete btn">Remove Book</a>
</div>
</div>
`);
swiper.update();
}
static deleteBook(element) {
if (element.classList.contains("delete")) {
element.parentElement.parentElement.remove();
}
//INIT SWIPER ONLY AFTER ADDING BOOKS
let swiper = document.querySelector(".swiper-container").swiper;
swiper.update();
}
static clearFields() {
document.querySelector("#result-form").reset();
}
}
// STORAGE CLASS
//EVENT: DISPLAY BOOKS
document.addEventListener("DOMContentLoaded", UI.displayBooks);
//EVENT ADD A BOOK
function getMovies(searchText) {
axios
.get(
`https://openlibrary.org/api/books?bibkeys=ISBN:${searchText}&format=json&jscmd=data`
)
.then((response) => {
console.log(response);
let path = `ISBN:${searchText}`;
let data = response.data[`${path}`];
let cover;
if (data.cover) {
cover = data.cover.medium;
} else {
cover = "./imgs/book-cover-placeholder.png";
}
let url = data.url;
let title = data.title;
let author = data.authors[0].name;
let pages = data.number_of_pages;
let isbn = searchText;
const book = new Book(url, cover, title, author, pages, isbn);
UI.addBookToList(book);
})
.catch((err) => {
console.log(err);
});
}
// EVENT: REMOVE A BOOK
document.querySelector(".swiper-wrapper").addEventListener("click", (e) => {
UI.deleteBook(e.target);
});
//SCANNER CODE
document.getElementById("start-button").addEventListener("click", function () {
document.getElementById("search-input").value = "";
let selectedDeviceId;
const codeReader = new ZXing.BrowserBarcodeReader();
console.log("ZXing code reader initialized");
//GET THE VIDEO DEVICE
codeReader
.getVideoInputDevices()
.then((videoInputDevices) => {
const sourceSelect = document.getElementById("sourceSelect");
selectedDeviceId = videoInputDevices[0].deviceId;
if (videoInputDevices.length > 1) {
videoInputDevices.forEach((element) => {
const sourceOption = document.createElement("option");
sourceOption.text = element.label;
sourceOption.value = element.deviceId;
sourceSelect.appendChild(sourceOption);
});
sourceSelect.onchange = () => {
selectedDeviceId = sourceSelect.value;
};
// const sourceSelectPanel = document.getElementById("sourceSelectPanel");
// sourceSelectPanel.style.display = "block";
}
//DECODE BARCODE FROM CAMERA
(() => {
codeReader
.decodeOnceFromVideoDevice(selectedDeviceId, "video")
.then((result) => {
let searchText = result.text;
console.log(result);
document.getElementById("search-input").value = searchText;
//GET MOVIES WHEN ADD BUTTON IS CLICKED
document
.getElementById("add-button")
.addEventListener("click", () => {
getMovies(searchText);
UI.clearFields();
});
})
.catch((err) => {
console.error(err);
});
})();
document.getElementById("stop-button").addEventListener("click", () => {
codeReader.reset();
console.log("Reset.");
});
})
.catch((err) => {
console.error(err);
});
});
The problem is probably because of the position of the following part of code:
document
.getElementById("add-button")
.addEventListener("click", () => {
getMovies(searchText);
UI.clearFields();
});
This snippet of code adds an event listener to the #add-button
element every time it's run, i.e. after every scan.
To make your code work, move it to a top-level DOMContentLoaded
listener, and change it into the following:
document
.getElementById("add-button")
.addEventListener("click", () => {
const searchText = document.getElementById("search-input").value;
if(!searchText) return;
getMovies(searchText);
UI.clearFields();
});
So, you should have something like this:
//SWIPER SETTING
document.addEventListener("DOMContentLoaded", () => {
const swiper = new Swiper(".swiper-container", {
effect: "coverflow",
grabCursor: true,
centeredSlides: true,
slidesPerView: "auto",
coverflowEffect: {
rotate: 60,
stretch: 0,
depth: 150,
modifier: 1,
slideShadows: true,
},
});
});
//BOOK CLASS
class Book {
constructor(url, cover, title, author, pages, isbn) {
this.url = url;
this.cover = cover;
this.title = title;
this.author = author;
this.pages = pages;
this.isbn = isbn;
}
}
//UI CLASS
class UI {
static displayBooks() {
// let swiper = document.querySelector(".swiper-container").swiper;
// swiper.removeAllSlides();
const storedBooks = [
{
url: "www.google.com",
title: "Test Title",
author: "Test Author",
pages: "230",
isbn: "9778621466124",
},
{
url: "www.isbndb.com",
title: "Test Title",
author: "Test Author",
pages: "100",
isbn: "9778621466124",
},
];
const books = storedBooks;
books.forEach((book) => UI.addBookToList(book));
}
static addBookToList(book) {
//INIT SWIPER ONLY AFTER ADDING BOOKS
let swiper = document.querySelector(".swiper-container").swiper;
swiper.appendSlide(`
<div class="swiper-slide">
<div class="book-cover">
<img src="${book.cover}" alt="" />
</div>
<div class="book-info">
<h2 class="book-title">${book.title}</h2>
<h3 class="book-author">By ${book.author}</h3>
<p class="book-pages">Pages: ${book.pages}</p>
<p>ISBN: ${book.isbn}</p>
</div>
<div class="cta-buttons">
<a href="${book.url}" class="read-more btn" target="_blank">Read More</a>
<a href="#" class="delete btn">Remove Book</a>
</div>
</div>
`);
swiper.update();
}
static deleteBook(element) {
if (element.classList.contains("delete")) {
element.parentElement.parentElement.remove();
}
//INIT SWIPER ONLY AFTER ADDING BOOKS
let swiper = document.querySelector(".swiper-container").swiper;
swiper.update();
}
static clearFields() {
document.querySelector("#result-form").reset();
}
}
// STORAGE CLASS
//EVENT: DISPLAY BOOKS
document.addEventListener("DOMContentLoaded", UI.displayBooks);
//EVENT ADD A BOOK
function getMovies(searchText) {
axios
.get(
`https://openlibrary.org/api/books?bibkeys=ISBN:${searchText}&format=json&jscmd=data`
)
.then((response) => {
console.log(response);
let path = `ISBN:${searchText}`;
let data = response.data[`${path}`];
let cover;
if (data.cover) {
cover = data.cover.medium;
} else {
cover = "./imgs/book-cover-placeholder.png";
}
let url = data.url;
let title = data.title;
let author = data.authors[0].name;
let pages = data.number_of_pages;
let isbn = searchText;
const book = new Book(url, cover, title, author, pages, isbn);
UI.addBookToList(book);
})
.catch((err) => {
console.log(err);
});
}
// EVENT: REMOVE A BOOK
document.querySelector(".swiper-wrapper").addEventListener("click", (e) => {
UI.deleteBook(e.target);
});
//SCANNER CODE
document.getElementById("start-button").addEventListener("click", function () {
document.getElementById("search-input").value = "";
let selectedDeviceId;
const codeReader = new ZXing.BrowserBarcodeReader();
console.log("ZXing code reader initialized");
//GET THE VIDEO DEVICE
codeReader
.getVideoInputDevices()
.then((videoInputDevices) => {
const sourceSelect = document.getElementById("sourceSelect");
selectedDeviceId = videoInputDevices[0].deviceId;
if (videoInputDevices.length > 1) {
videoInputDevices.forEach((element) => {
const sourceOption = document.createElement("option");
sourceOption.text = element.label;
sourceOption.value = element.deviceId;
sourceSelect.appendChild(sourceOption);
});
sourceSelect.onchange = () => {
selectedDeviceId = sourceSelect.value;
};
// const sourceSelectPanel = document.getElementById("sourceSelectPanel");
// sourceSelectPanel.style.display = "block";
}
//DECODE BARCODE FROM CAMERA
(() => {
codeReader
.decodeOnceFromVideoDevice(selectedDeviceId, "video")
.then((result) => {
let searchText = result.text;
console.log(result);
document.getElementById("search-input").value = searchText;
})
.catch((err) => {
console.error(err);
});
})();
document.getElementById("stop-button").addEventListener("click", () => {
codeReader.reset();
console.log("Reset.");
});
})
.catch((err) => {
console.error(err);
});
});
document.addEventListener('DOMContentLoaded', () => {
//GET MOVIES WHEN ADD BUTTON IS CLICKED
document
.getElementById("add-button")
.addEventListener("click", () => {
const searchText = document.getElementById("search-input").value;
if(!searchText) return;
getMovies(searchText);
UI.clearFields();
});
});