Dynamic Audio Widgets playing same audio - javascript

Here is a simplified version of my web page - all buttons are playing the first element in the array and I don't know why. I suspect it's something to do with the forEach loop not functioning in the correct way
// Listen Page
audioItems = [
'http://codeskulptor-demos.commondatastorage.googleapis.com/GalaxyInvaders/alien_hit.wav',
'http://codeskulptor-demos.commondatastorage.googleapis.com/GalaxyInvaders/alien_shoot.wav',
'http://codeskulptor-demos.commondatastorage.googleapis.com/GalaxyInvaders/bonus.wav',
]
const listenContainer = document.querySelector('.audio-widget-container');
audioItems.forEach(track => {
newTrackWidget = document.createElement('div');
newTrackWidget.classList.add('track-container')
trackTitle = document.createElement("h3");
trackTitle.textContent = track;
trackButton = document.createElement("button");
trackButton.innerHTML = `<i class="fa-solid fa-play"></i>`
trackTimeline = document.createElement("div");
trackAudio = document.createElement("audio");
trackAudio.src = track;
trackButton.addEventListener("click", () => {
trackAudio.play()
})
newTrackWidget.appendChild(trackAudio)
newTrackWidget.appendChild(trackTitle);
newTrackWidget.appendChild(trackButton);
newTrackWidget.appendChild(trackTimeline);
listenContainer.appendChild(newTrackWidget);
});
<script src="https://kit.fontawesome.com/00c3160031.js" crossorigin="anonymous"></script>
<section class="audio-widget-container">
</section>

You assigned to variables without keywords, which mean that they are hoisted to global scope.Therefore the sound was overwritten. To fix it create new variables inside forEach loop using keyword let to make them unique:
const audioItems = [
'http://codeskulptor-demos.commondatastorage.googleapis.com/GalaxyInvaders/alien_hit.wav',
'http://codeskulptor-demos.commondatastorage.googleapis.com/GalaxyInvaders/alien_shoot.wav',
'http://codeskulptor-demos.commondatastorage.googleapis.com/GalaxyInvaders/bonus.wav',
];
const listenContainer = document.querySelector('.audio-widget-container');
audioItems.forEach(track => {
let newTrackWidget = document.createElement('div');
newTrackWidget.classList.add('track-container');
let trackTitle = document.createElement('h3');
trackTitle.textContent = track;
let trackButton = document.createElement('button');
trackButton.innerHTML = `<i class="fa-solid fa-play"></i>`;
let trackTimeline = document.createElement('div');
let trackAudio = document.createElement('audio');
trackAudio.src = track;
trackButton.addEventListener('click', () => {
trackAudio.play();
});
newTrackWidget.appendChild(trackAudio);
newTrackWidget.appendChild(trackTitle);
newTrackWidget.appendChild(trackButton);
newTrackWidget.appendChild(trackTimeline);
listenContainer.appendChild(newTrackWidget);
});
<script src="https://kit.fontawesome.com/00c3160031.js" crossorigin="anonymous"></script>
<section class="audio-widget-container">
</section>

Related

Element not being created on onclick

I'm trying to make one element disapear and another be created on another part of my document. When the item that I want to disapear is clicked it disapears, however the element I want to create isn't created. I have definitly appended it to the correct element so I'm very confused as to why it doesnt appear in chrome dev tools or on the page. Here's the code and thanks for any help.
let hideGridItem = document.addEventListener('click', function(e) {
let hideItem = (e.target.parentElement.parentElement.id)
let hideItemTwo = (e.target.parentElement.id)
let hideItemThree = (e.target.id)
const displayedItemWrapper = document.getElementById('selected-item-container')
let hiddenItem = document.getElementById(hideItem)
if(hideItem.includes('grid-item')){
hiddenItem.style.display = 'none';
let hiddednItemWeapon = (hiddenItem.childNodes[2].childNodes[0].innerHTML);
let hiddednItemCondition = (hiddenItem.childNodes[0].childNodes[0].innerHTML);
let hiddenItemImg = (hiddenItem.childNodes[1].childNodes[0].src);
let hiddednItemWeaponName = (hiddenItem.childNodes[2].childNodes[1].innerHTML);
let hiddednItemWeaponPrice = (hiddenItem.childNodes[2].childNodes[2].innerHTML);
let displayedItemContainer = document.createElement('div');
displayedItemContainer.className = 'selected-item-container';
let displayedItemImgContainer = document.createElement('div');
displayedItemImgContainer.className = 'selected-item-img-container';
let displayedItemImg = document.createElement('img');
displayedItemImg.src = hiddenItemImg;
displayedItemWrapper.appendChild.displayedItemContainer;
displayedItemContainer.appendChild.displayedItemImgContainer;
displayedItemImgContainer.appendChild.displayedItemImgContainer;
}
let hiddenItemTwo = document.getElementById(hideItemTwo)
if(hideItemTwo.includes('grid-item') ){
hiddenItemTwo.style.display = 'none'
}
let hiddenItemThree = document.getElementById(hideItemThree)
if(hideItemThree.includes('grid-item') ){
hiddenItemThree.style.display = 'none'
}
}, false);

How to achieve individual card functionality with JS?

Repo: https://github.com/bigolboyyo/weatherdbapp
Hello! I am working on a small project working with an API for the first time. I'm making a simple weather app that allows you to type in a location and append a "card" of data.
This card has a "front" and "back". Front being the weather data and the back being a simple textarea notepad.
As far as functionality goes I am just about there. My only issue is now when I have more than one card I only get an event listener on the first card button and the first card's button affects all the other cards created after! How would I apply the event listener for each card's buttons made and have those buttons only affect the relevant card?
Current code I'm working with
let notesBtn = document.getElementById("notes-btn");
notesBtn.addEventListener("click", handleFrontClick);
console.log(notesBtn);
function handleFrontClick(e) {
e.target.parentNode.remove();
card.appendChild(back);
let frontBtn = document.getElementById("front-btn");
frontBtn.addEventListener("click", (e) => {
e.target.parentNode.remove();
card.appendChild(front);
});
ALL JS (git repo above)
const body = document.getElementsByTagName("body");
const unsplashRandom =
"https://source.unsplash.com/random/1920x1080/?landscapes";
document.body.style.backgroundImage = `url(${unsplashRandom})`;
const header = document.createElement("div");
header.className = "header";
header.id = "header";
document.body.append(header);
const container = document.createElement("div");
container.className = "container";
document.body.append(container);
const h1 = document.createElement("h1");
h1.id = "h1";
h1.textContent = "WeatherDB Search";
h1.style.textAlign = "center";
header.append(h1);
let p1 = document.createElement("p");
p1.id = "p1";
p1.textContent = "Enter location for today's weather!";
p1.style.textAlign = "center";
header.append(p1);
const cityInput = document.createElement("input");
cityInput.className = "search-inputs";
cityInput.id = "city-input";
cityInput.placeholder = " ex: New York, Toronto, London";
header.append(cityInput);
const searchButton = document.createElement("button");
searchButton.className = "buttons";
searchButton.id = "search-btn";
searchButton.textContent = "🔍";
let br = document.createElement("br");
header.append(br);
header.append(searchButton);
function fetchWeather(e) {
let userInput = cityInput.value;
let config = {
Headers: {
"Content-type": "application/json",
},
};
return fetch(
`https://weatherdbi.herokuapp.com/data/weather/${userInput}`,
config
)
.then((res) => res.json())
.then((json) => weatherSearch(json, userInput))
.catch((err) => {
window.alert(
`${err} \n https://weatherdbi.herokuapp.com/data/weather/${userInput}`
);
console.log(err);
});
}
function weatherSearch(e, userInput) {
cityInput.value = "";
let weatherKeys = Object.keys(e.currentConditions);
let weatherValues = Object.values(e.currentConditions);
let temps = Object.values(weatherValues[1]);
let speeds = Object.values(weatherValues[4]);
const card = document.createElement("div");
card.id = `${userInput}-card`;
card.className = "cards";
container.append(card);
const front = document.createElement("div");
front.id = `${userInput}-front`;
front.className = "front";
front.innerHTML = `
<h1>${userInput.toUpperCase()}</h1>
<p>Time: ${weatherValues[0]}</p>
<li id="comment">Comment: ${weatherValues[6]}</li>
<li id="precipitation">Precipitation: ${weatherValues[2]}
<li id="temp">Temp: ${temps[0]}c | ${temps[1]}f</li>
<li id="humidity">Humidity: ${weatherValues[3]}</li>
<li id="wind">Wind Speed: ${speeds[0]}km | ${speeds[1]}mile</li>
</br>
<button id="notes-btn" class="buttons">Notes</button>
`;
card.append(front);
const back = document.createElement("div");
back.id = `${userInput}-back`;
back.className = "back";
back.innerHTML = `
<textarea id="back-text" name="backtext"
rows="25" placeholder="NotePad">
</textarea>
</br>
<button id="front-btn">Flip</button>
`;
let notesBtn = document.getElementById("notes-btn");
notesBtn.addEventListener("click", handleFrontClick);
console.log(notesBtn);
function handleFrontClick(e) {
e.target.parentNode.remove();
card.appendChild(back);
let frontBtn = document.getElementById("front-btn");
frontBtn.addEventListener("click", (e) => {
e.target.parentNode.remove();
card.appendChild(front);
});
}
}
document.addEventListener("DOMContentLoaded", (e) => {
searchButton.addEventListener("click", (e) => {
fetchWeather();
});
document.addEventListener("keydown", function (e) {
if (e.key === "Enter" && cityInput.value.length > 0) {
fetchWeather();
}
});
});

How do I display every element from my localStorage item?

i'm trying to create a simple To-do list, and my question is how do i get all elements of a single item in localStorage displayed?
pushing things into localStorage in a form of an array works fine, but only thing I see on my page is the first index of the "tasks" array.
const inputEl = document.getElementById("inputEl")
const submitBtn = document.getElementById("submit")
const clearBtn = document.getElementById("clearBtn")
const todoListContainer = document.getElementById("todoList")
const taskContainer = document.querySelector(".task")
const cancelBtn = document.querySelector(".cancelBtn")
const doneBtn = document.querySelector(".doneBtn")
const errorMsg = document.querySelector(".error")
let localStorageContent = localStorage.getItem("tasks")
let tasks = []
function createTask(){
if(inputEl.value.length != 0){
const newDiv = document.createElement("div")
newDiv.classList.add("task")
const newParagraph = document.createElement("p")
const newCancelBtn = document.createElement("button")
newCancelBtn.classList.add("cancelBtn")
newCancelBtn.textContent = "X"
const newDoneBtn = document.createElement("button")
newDoneBtn.classList.add("doneBtn")
newDoneBtn.textContent = "Done"
todoListContainer.appendChild(newDiv)
newDiv.appendChild(newParagraph)
newDiv.appendChild(newCancelBtn)
newDiv.appendChild(newDoneBtn)
//^^ Creating a container for a new task, with all its elements and assigning the classes^^
tasks.push(inputEl.value)
localStorage.setItem("tasks", JSON.stringify(tasks))
inputEl.value = ""
newParagraph.textContent = JSON.parse(localStorageContent)
errorMsg.textContent = ""
}else{
errorMsg.textContent = "You have to type something in!"
errorMsg.classList.toggle("visibility")
}
}
submitBtn.addEventListener("click", () =>{
createTask()
})
When you execute JSON.parse(localStorageContent) you convert your string into an array, that's right.
But:
newParagraph.textContent = JSON.parse(localStorageContent)
is the same as:
newParagraph.textContent = JSON.parse(localStorageContent)[0]
So, you have to loop through your JSON.parse(localStorageContent)
array... Therefore, create a new variable:
let tasksItem = JSON.parse(localStorageContent)
and loop on with .forEach method

Display CSS GRID with Javascript & DOM

I am trying to create a number of DOM item's using data from my firebase Firestore, however upon running, I have an error in line 34. I am just not quite sure what I should append resultGrid to, to achieve what I am looking for.
<div class="w-layout-grid grid">
<div class="result div-block">
<div class="data-image"></div>
<div class="result-footer">
<div class="results-text">
<h5 class="data-text">Taffy, 8 | Arabian</h5>
<h5 class="data-text">$12,000</h5>
This is my current javascript.
const resultList = document.querySelector('#horseList')
function renderResult(doc){
var resultGrid = document.createElement('div');
resultGrid.className = ('w-layout-grid grid');
var resultDiv = document.createElement('div');
resultDiv.className = ('result');
var resultImage = document.createElement('div');
resultImage.className = ('data-image');
var resultFooter = document.createElement('div');
resultFooter.className = ('result-footer');
var resultText = document.createElement('div');
resultText.className = ('results-text');
var resultButton = document.createElement('button');
resultButton.className = ('button tiny w-button');
resultButton.innerHTML = "View";
//Render text from database inside H5
const string = (`${doc.data().name}, ${doc.data().age} | ${doc.data().type}`);
let resultOne = document.createElement('h5');
let price = document.createElement('h5');
resultOne.className = ('data-text');
price.className = ('data-text');
price.textContent = (`$${doc.data().price}`);
resultOne.textContent = string;
resultList.appendChild(resultGrid);
resultGrid.appendChild(resultDiv);
resultDiv.appendChild(resultImage);
resultDiv.appendChild(resultFooter);
resultFooter.appendChild(resultText);
resultFooter.appendChild(resultButton);
resultText.appendChild(resultOne);
resultText.appendChild(price);
}
//connect to database & get data
const db = firebase.firestore();
db.collection("Horses").get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
// doc.data() is never undefined for query doc snapshots
renderResult(doc);
});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
Thanks in advance!
I figured it out! I ended up making the div GRID statically in HTML then I just set javascript to create the dynamic div's within that grid!
HTML
<div id="horseList" class="w-layout-grid grid"></div>
Javascript
const resultList = document.querySelector('#horseList')
function renderResult(doc){
var resultDiv = document.createElement('div');
resultDiv.className = ('result');
resultDiv.setAttribute('data-id', doc.id);
var resultImage = document.createElement('div');
resultImage.className = ('data-image');
var resultFooter = document.createElement('div');
resultFooter.className = ('result-footer');
var resultText = document.createElement('div');
resultText.className = ('results-text');
var resultButton = document.createElement('button');
resultButton.className = ('button tiny w-button');
resultButton.innerHTML = "View";
//Render text from database inside H5
const string = (`${doc.data().name}, ${doc.data().age} | ${doc.data().type}`);
let resultOne = document.createElement('h5');
let price = document.createElement('h5');
resultOne.className = ('data-text');
price.className = ('data-text');
price.textContent = (`$${doc.data().price}`);
resultOne.textContent = string;
resultList.appendChild(resultDiv);
resultDiv.appendChild(resultImage);
resultDiv.appendChild(resultFooter);
resultFooter.appendChild(resultText);
resultFooter.appendChild(resultButton);
resultText.appendChild(resultOne);
resultText.appendChild(price);
}

How do I create an edit function inside of a list?

Currently I have a delete function already which is shown by a cross
So how I am doing this is by using a function that renders in the information from another database, into this list.
function renderCafe(doc){
let li = document.createElement('li');
let name = document.createElement('span');
let city = document.createElement('span');
let cross = document.createElement('div');
li.setAttribute('data-id', doc.id);
name.textContent = doc.data().name;
city.textContent = doc.data().city;
cross.textContent = 'x';
li.appendChild(name);
li.appendChild(city);
li.appendChild(cross);
cafeList.appendChild(li);
// deleting data
cross.addEventListener('click', (e) => {
e.stopPropagation();
let id = e.target.parentElement.getAttribute('data-id');
db.collection('cafes').doc(id).delete();
});}
So how do I create the edit button function like this delete one? Should it also be an EventListener that occurs when clicked?
I was thinking to have an edit button, when clicked, changes into "Update", while the static information turns into a text box with the current values.
Then when the "Update" is being clicked, it then saves the information.
I am a student, and have no prior knowledge in javascript, i took these codes from tutorials that I can find online.
Here's a possible solution, similar to your style.
Replace the alert with a database update.
Make sure you're listening for document changes, and updating your UI accordingly, so that the new name and city will replace your old ones.
const cafeList = document.getElementById('cafe-list');
const sampleCafes = [
{ data: () => ({ name: 'new cafe', city: 'new city'}), id: 'sample-cafe' },
];
sampleCafes.forEach(cafe => renderCafe(cafe));
function renderCafe(doc){
let li = document.createElement('li');
let name = document.createElement('p');
let city = document.createElement('p');
let nameInput = document.createElement('input');
let cityInput = document.createElement('input');
let edit = document.createElement('div');
let submit = document.createElement('div');
let cross = document.createElement('div');
li.setAttribute('data-id', doc.id);
name.textContent = doc.data().name;
city.textContent = doc.data().city;
nameInput.value = name.textContent;
cityInput.value = city.textContent;
nameInput.hidden = true;
cityInput.hidden = true;
edit.textContent = 'edit';
submit.textContent = 'submit';
submit.hidden = true;
cross.textContent = 'x';
li.appendChild(name);
li.appendChild(city);
li.appendChild(nameInput);
li.appendChild(cityInput);
li.appendChild(edit);
li.appendChild(submit);
li.appendChild(cross);
cafeList.appendChild(li);
// deleting data
cross.addEventListener('click', (e) => {
e.stopPropagation();
let id = e.target.parentElement.getAttribute('data-id');
alert(`db.collection('cafes').doc(id).delete();`);
});
// editing data
edit.addEventListener('click', (e) => {
e.stopPropagation();
nameInput.hidden = false;
cityInput.hidden = false;
submit.hidden = false;
name.hidden = true;
city.hidden = true;
edit.hidden = true;
});
// submitting new data
submit.addEventListener('click', (e) => {
e.stopPropagation();
const id = e.target.parentElement.getAttribute('data-id');
nameInput.hidden = true;
cityInput.hidden = true;
submit.hidden = true;
name.hidden = false;
city.hidden = false;
edit.hidden = false;
const newName = nameInput.value;
const newCity = cityInput.value;
alert(`TODO: update doc '${id}' to\n{name: '${newName}', city: '${newCity}'}.`);
});
}
<ul id="cafe-list"></ul>

Categories