The below is part of a media player. Unfortunately, I cannot find the reason why the event listener is not registering the clicks on the hearts (when a user favorites a song). I have tried several implementations and I am researching for the last week with no success. Can you help?
How can I make the click listener to update the heart count?
HTML
<div class="player">
<div class="dashboard">
<header>
<p>Playing:</p>
</header>
<div class="cd">
<div class="cd-thumb">
</div>
</div>
<div class="control">
<div class="btn btn-random inactive">
<i class="fas fa-random"></i>
</div>
<div class="btn btn-prev">
<i class="fas fa-step-backward"></i>
</div>
<div class="btn btn-toggle-play">
<i class="fas fa-pause icon-pause"></i>
<i class="fas fa-play icon-play"></i>
</div>
<div class="btn btn-next">
<i class="fas fa-step-forward"></i>
</div>
<div class="btn btn-mute-unmute inactive">
<i class="fas fa-volume-up"></i>
</div>
</div>
</div>
<div class="playlist">
</div>
Script 1
render: function () {
let that = this;
fetch("hearts.txt")
.then(function(response) {
return response.json();
})
.then(function(heartCounts) {
let t = that.songs.map(
(t, e) => `
<div class="song ${
e === that.currentIndex ? "active" : ""
}" data-index="${e}">
<div class="thumb"
style="background-image: url('${t.image}')">
</div>
<div class="body">
<h3 class="title">${t.name}</h3>
<p class="author">${t.singer}</p>
</div>
<div class="heart" data-song-id="${e}">
<i class="fa fa-heart${
heartCounts[e] ? " active" : ""
}"></i> <span>${heartCounts[e] || 0}</span>
</div>
</div>
`
);
playlist.innerHTML = t.join("");
});
},
Script 2
const getHeartCounts = function () {
let xhr = new XMLHttpRequest();
xhr.open("GET", "return.php", true);
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
let heartCounts = JSON.parse(xhr.responseText);
// Update the heart count displays
document.querySelectorAll(".heart i + span").forEach((countDisplay, i) => {
countDisplay.innerHTML = heartCounts[i];
});
// Update the active heart icons
document.querySelectorAll(".heart i").forEach((heart, i) => {
if (heartCounts[i] > 0) {
heart.classList.add("active");
}
});
}
};
xhr.send();
};
document.addEventListener("DOMContentLoaded", function () {
// Add click listener to update the heart count
document.querySelectorAll(".heart").forEach(function (heart) {
heart.addEventListener("click", function (e) {
let target = e.target,
songIndex = parseInt(target.dataset.songId),
countEl = target.querySelector("span"),
heartCount = countEl ? parseInt(countEl.innerHTML) : 0,
isActive = target.classList.contains("active");
// Update the heart count
heartCount = isActive ? heartCount - 1 : heartCount + 1;
if (countEl) {
countEl.innerHTML = heartCount;
}
let heartIcon = target.querySelector("i");
if (heartIcon) {
heartIcon.classList.toggle("active", !isActive);
}
// Update the heart count on the server
let xhr = new XMLHttpRequest();
xhr.open("POST", "store.php", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send("index=" + songIndex + "&count=" + heartCount);
});
});
// Update the heart counts on page load
getHeartCounts();
});
Related
I'm new in Django, im create product page in Django in product card i add increment decrement add to cart button, but add to cart button work on first product card so,what can i do?
I'm added add to card increment and decrement button using JavaScript why JavaScript only apply on one card product in Django. so, how to resolve when ever i use looping in Django my JavaScript not working or shown in console btn varriable already declared.
{% for i in productskey %}
<div class="col-sm-4">
<div class="product-image-wrapper" style="border:1px solid #ddd ;">
<div class="single-products">
<div class="productinfo text-center">
<a href="{% url 'prodetail' i.id %}">
<img src=" {{i.productImage.url}} " alt="" style="width:255px; height:255px"/>
</a>
<h2>₹{{i.productPrice}}</h2>
<p>{{i.productName}}</p>
<div class="btn">
<button class="mainusbtn">-</button>
<button class="mainbtn" class="btn btn-default" class="fa fa-shopping-cart">ADD TO CART</button>
<button class="plusbtn">+</button>
</div>
<!-- <i class="fa fa-shopping-cart"></i>Add to cart -->
</div>
<!-- <div class="product-overlay">
<div class="overlay-content">
<h2>$56</h2>
<p>Easy Polo Black Edition</p>
<i class="fa fa-shopping-cart"></i>Add to cart
</div>
</div> -->
</div>
<!-- <div class="choose">
<ul class="nav nav-pills nav-justified">
<li><i class="fa fa-plus-square"></i>Add to wishlist</li>
<li><i class="fa fa-plus-square"></i>Add to compare</li>
</ul>
</div> -->
</div>
<!-- </a> -->
</div>
<script>
let btn = document.querySelector('.mainbtn');
let pbtn = document.querySelector('.plusbtn');
let mbtn = document.querySelector('.mainusbtn');
btn.addEventListener("click", () => {
if (btn.innerText == 'ADD TO CART') {
btn.innerText = 1;
pbtn.style.display = 'inline-block';
mbtn.style.display = 'inline-block';
}
})
mbtn.addEventListener("click", () => {
if (btn.innerText == 5) {
pbtn.style.display = 'inline-block';
}
if (btn.innerText < 2) {
btn.innerText = 'ADD TO CART';
pbtn.style.display = 'none';
mbtn.style.display = 'none';
} else {
btn.innerText = btn.innerText - 1;
}
})
pbtn.addEventListener("click", () => {
btn.innerText = +(btn.innerText) + +1;
// if(btn.innerText == 5){
// pbtn.style.display = 'none';
// }
})
</script>
I am building a music player using HTML-css-javascript only. I have a list of music which I have stored in an array as shown below.
let songs = [
{songName: "Butter", filePath:"songs/1.mp3",coverPath:'covers/1.png'},
{songName: "Boy With Luv", filePath:"songs/2.mp3",coverPath:'covers/2.jpeg'},
{songName: "Dynamite", filePath:"songs/3.mp3",coverPath:'covers/3.jpeg'},
{songName: "Idol", filePath:"songs/4.mp3",coverPath:'covers/4.png'},
{songName: "Life Goes On", filePath:"songs/5.mp3",coverPath:'covers/5.jpeg'},
{songName: "Mic Drop", filePath:"songs/6.mp3",coverPath:'covers/6.jpeg'},
]
And the UI looks like this:
Things look good that on clicking specific songs, the play icon on the side of the song name changes, but when I change the song with the next button at the bottom, the song changes but the icon still remains unchanged. As you can see in the second image, I changed to the fourth song but the music list still shows the third song playing.
I need to change the styling of the icon with the click of the next&previous button at the bottom. I have songindex of the current song playing in the function.
for reference attached the nextButton code:
//next button
document.getElementById('next').addEventListener('click', (e)=>{
songIndex = (songIndex >=5) ? 0 : songIndex+1;
audioElement.src = `songs/${songIndex}.mp3`;
audioElement.currentTime = 0;
audioElement.play();
masterPlay.classList.remove('fa-play-circle');
masterPlay.classList.add('fa-pause-circle');
mastersongName.innerText = "BTS - "+songs[songIndex].songName;
masterSideImg.src = songs[songIndex].coverPath;
})
COmplete HTML 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">
<title>Redify - listen music here</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<nav>
<ul>
<li class="brand"><img src="logo.png" alt="logo">Spotify</li>
<li>Home</li>
<li>About</li>
</ul>
</nav>
<div class="container">
<div class="songList">
<h1>Best of BTS</h1>
<div class="songitemContainer">
<div class="songItem">
<img alt="1">
<span class="songName">Butter</span>
<span class="songlistPlay">
<span class="timestamp">3:41
<i id="0" class="far songItemPlay fa-1.5x fa-play-circle"></i>
</span>
</span>
</div>
<div class="songItem">
<img alt="1">
<span class="songName">Butter</span>
<span class="songlistPlay">
<span class="timestamp">3:41
<i id="1" class="far songItemPlay fa-1.5x fa-play-circle"></i>
</span>
</span>
</div>
<div class="songItem">
<img alt="1">
<span class="songName">Butter</span>
<span class="songlistPlay">
<span class="timestamp">3:41
<i id="2" class="far songItemPlay fa-1.5x fa-play-circle"></i>
</span>
</span>
</div>
<div class="songItem">
<img alt="1">
<span class="songName">Butter</span>
<span class="songlistPlay">
<span class="timestamp">3:41
<i id="3" class="far songItemPlay fa-1.5x fa-play-circle"></i>
</span>
</span>
</div>
<div class="songItem">
<img alt="1">
<span class="songName">Butter</span>
<span class="songlistPlay">
<span class="timestamp">3:41
<i id="4" class="far songItemPlay fa-1.5x fa-play-circle"></i>
</span>
</span>
</div>
<div class="songItem">
<img alt="1">
<span class="songName">Butter</span>
<span class="songlistPlay">
<span class="timestamp">3:41
<i id="5" class="far songItemPlay fa-1.5x fa-play-circle"></i>
</span>
</span>
</div>
</div>
</div>
<div class="songBanner">
</div>
</div>
<div class="bottom">
<img src="covers/1.png" alt="side_img" width="60px" id="masterSideImg">
<div class="bottomprogress">
<input type="range" name="range" id="myProgressBar" min="0" max="100" value=0>
<div class="icons">
<i class="fas fa-2x fa-step-backward" id="previous"></i>
<i class="far fa-2x fa-play-circle" id="masterPlay"></i>
<i class="fas fa-2x fa-step-forward" id="next"></i>
</div>
<div class="songInfo">
<img src="playing.gif" width="32px" alt="playing" id="gif"><span id="mastersongName">BTS - Butter</span>
</div>
</div>
</div>
<script src="https://kit.fontawesome.com/06646b7200.js" crossorigin="anonymous"></script>
<script src="script.js"></script>
The complete link of the project is given here: https://github.com/mohitm15/Redify
I refactored your play/pause icon setting part into a function:
const ICON_PLAY = 1;
const ICON_STOP = 0;
const handleSongPlayIcon = (target, isPlaying) => {
if (isPlaying == ICON_PLAY) {
target.classList.remove("fa-pause-circle");
target.classList.add("fa-play-circle");
} else if (isPlaying == ICON_STOP) {
target.classList.remove("fa-play-circle");
target.classList.add("fa-pause-circle");
}
};
and adjusted several functions in your code, toggling the icons
//Variable initialisation
let songIndex = 0;
let audioElement = new Audio("songs/0.mp3");
let masterPlay = document.getElementById("masterPlay");
let myProgressBar = document.getElementById("myProgressBar");
let gif = document.getElementById("gif");
let songItems = Array.from(document.getElementsByClassName("songItem"));
let songTitle = document.getElementsByClassName("songInfo");
let mastersongName = document.getElementById("mastersongName");
let masterSideImg = document.getElementById("masterSideImg");
//console.log(songTitle.innerText)
let songs = [
{ songName: "Butter", filePath: "songs/1.mp3", coverPath: "covers/1.png" },
{
songName: "Boy With Luv",
filePath: "songs/2.mp3",
coverPath: "covers/2.jpeg",
},
{ songName: "Dynamite", filePath: "songs/3.mp3", coverPath: "covers/3.jpeg" },
{ songName: "Idol", filePath: "songs/4.mp3", coverPath: "covers/4.png" },
{
songName: "Life Goes On",
filePath: "songs/5.mp3",
coverPath: "covers/5.jpeg",
},
{ songName: "Mic Drop", filePath: "songs/6.mp3", coverPath: "covers/6.jpeg" },
];
const ICON_PLAY = 1;
const ICON_STOP = 0;
const handleSongPlayIcon = (target, isPlaying) => {
if (isPlaying == ICON_PLAY) {
target.classList.remove("fa-pause-circle");
target.classList.add("fa-play-circle");
} else if (isPlaying == ICON_STOP) {
target.classList.remove("fa-play-circle");
target.classList.add("fa-pause-circle");
}
};
songItems.forEach((item, i) => {
//console.log(item, i);
item.getElementsByTagName("img")[0].src = songs[i].coverPath;
item.getElementsByClassName("songName")[0].innerText = songs[i].songName;
//item.getElementsByClassName("timestamp")[0].innerText = parseInt(audioElement.duration*1);
});
//audioElement.play();
//handle play/pause
masterPlay.addEventListener("click", () => {
// grab all the icon elements on the page
const allSongsPlayItemElements = document.querySelectorAll(".songItemPlay");
if (audioElement.paused || audioElement.currentTime <= 0) {
audioElement.play();
handleSongPlayIcon(masterPlay, ICON_STOP);
handleSongPlayIcon(allSongsPlayItemElements[songIndex], ICON_STOP);
gif.style.opacity = 1;
} else {
audioElement.pause();
handleSongPlayIcon(masterPlay, ICON_PLAY);
handleSongPlayIcon(allSongsPlayItemElements[songIndex], ICON_PLAY);
gif.style.opacity = 0;
}
});
//Listen to event
audioElement.addEventListener("timeupdate", () => {
//update songbar
progress = parseInt((audioElement.currentTime / audioElement.duration) * 100);
myProgressBar.value = progress;
});
myProgressBar.addEventListener("change", () => {
audioElement.currentTime =
(myProgressBar.value * audioElement.duration) / 100;
});
//songlist appear on landing page
//makes button of all songs to display play symbol
const makeAllPlay = () => {
Array.from(document.getElementsByClassName("songItemPlay")).forEach(
(item) => {
handleSongPlayIcon(item, ICON_PLAY);
}
);
};
//songList :eventListener for song playing/pausing
let selectedSongIndex;
Array.from(document.getElementsByClassName("songItemPlay")).forEach((item) => {
item.addEventListener("click", (e) => {
//console.log(e.target) gives => <i class="far songItemPlay fa-1.5x fa-pause-circle" aria-hidden="true"></i>
makeAllPlay();
songIndex = parseInt(e.target.id);
//songIndex = the song is playing currently (for first time)
//selectedsongIndex = the song that user clicked when any song is playing (after first time)
if (audioElement.paused === true) {
selectedSongIndex = songIndex;
audioElement.src = `songs/${songIndex}.mp3`;
audioElement.currentTime = 0;
audioElement.play();
//console.log("song is playing" );
handleSongPlayIcon(e.target, ICON_STOP);
handleSongPlayIcon(masterPlay, ICON_STOP);
gif.style.opacity = 1;
} else if (audioElement.paused === false) {
if (selectedSongIndex === songIndex) {
audioElement.pause();
//console.log("audio is paused now")
handleSongPlayIcon(e.target, ICON_PLAY);
handleSongPlayIcon(masterPlay, ICON_PLAY);
gif.style.opacity = 0;
} else {
makeAllPlay();
songIndex = parseInt(e.target.id);
selectedSongIndex = songIndex;
audioElement.src = `songs/${songIndex}.mp3`;
audioElement.currentTime = 0;
audioElement.play();
//console.log("song is playing" );
handleSongPlayIcon(e.target, ICON_STOP);
handleSongPlayIcon(masterPlay, ICON_STOP);
gif.style.opacity = 1;
}
}
//console.log("songIndexx = ",songIndex);
//console.log("selected = " +selectedSongIndex);
mastersongName.innerText = "BTS - " + songs[songIndex].songName;
masterSideImg.src = songs[songIndex].coverPath;
audioElement.addEventListener("timeupdate", () => {
// console.log("songcurrentTime = "+parseInt(audioElement.currentTime))
// console.log("song duartion = "+ parseInt(audioElement.duration));
if (audioElement.currentTime === audioElement.duration) {
makeAllPlay();
console.log("song Completed");
songIndex = songIndex >= 5 ? 0 : songIndex + 1;
audioElement.src = `songs/${songIndex}.mp3`;
audioElement.currentTime = 0;
audioElement.play();
mastersongName.innerText = "BTS - " + songs[songIndex].songName;
masterSideImg.src = songs[songIndex].coverPath;
// grab all the icon elements on the page
const allSongsPlayItemElements = document.querySelectorAll(".songItemPlay");
handleSongPlayIcon(allSongsPlayItemElements[songIndex], ICON_STOP);
//previous can be accessed with e.target.id
//how to access the next element (as it is not being clicked by user)
}
});
});
});
//next button
document.getElementById("next").addEventListener("click", (e) => {
// grab all the icon elements on the page
const allSongsPlayItemElements = document.querySelectorAll(".songItemPlay");
// set the currently playing icon to "play"
handleSongPlayIcon(allSongsPlayItemElements[songIndex], ICON_PLAY);
songIndex = songIndex >= 5 ? 0 : songIndex + 1;
audioElement.src = `songs/${songIndex}.mp3`;
audioElement.currentTime = 0;
audioElement.play();
handleSongPlayIcon(masterPlay, ICON_STOP);
mastersongName.innerText = "BTS - " + songs[songIndex].songName;
masterSideImg.src = songs[songIndex].coverPath;
// set the next song icon to "stop"
handleSongPlayIcon(allSongsPlayItemElements[songIndex], ICON_STOP);
});
//previous button
document.getElementById("previous").addEventListener("click", (e) => {
// grab all the icon elements on the page
const allSongsPlayItemElements = document.querySelectorAll(".songItemPlay");
// set the currently playing icon to "play"
handleSongPlayIcon(allSongsPlayItemElements[songIndex], ICON_PLAY);
songIndex = songIndex <= 0 ? 5 : songIndex - 1;
audioElement.src = `songs/${songIndex}.mp3`;
audioElement.currentTime = 0;
audioElement.play();
handleSongPlayIcon(masterPlay, ICON_STOP);
mastersongName.innerText = "BTS - " + songs[songIndex].songName;
masterSideImg.src = songs[songIndex].coverPath;
// set the next song icon to "stop"
handleSongPlayIcon(allSongsPlayItemElements[songIndex], ICON_STOP);
});
Looks to me like you forgot to call makeAllPlay(); at the beginning of the previous/next button click handlers. Also you'll want to change the newly playing song item to show the pause button.
Near the beginning of the two click handlers (for previous/next buttons) add:
makeAllPlay();
const itemElem = document.getElementsByClassName("songItemPlay")[songIndex];
itemElem.classList.remove('fa-play-circle');
itemElem.classList.add('fa-pause-circle');
I want to make my own To Do list using JavaScript and localStorage. After writing the input and pressing the send button, the item will be added and display on the screen. A check button will appear next to the item. After pressing the check button, I want the class to be added. I want add css (opacity, line...). But when I press a check button, the console shows this error: Uncaught TypeError: Cannot read properties of undefined (reading 'add')
at check (app.js:23)
at HTMLElement.onclick (index.html:30)
HTML code:
<body>
<div class="main">
<input id="item" type="text">
<i id="addItem" class="fas fa-plus"></i><br> <br>
</div>
<div class="items">
<div id="addedItems"></div>
</div>
<i onclick='deleteAll()' class="fas fa-trash"></i>
</body>
JS code:
const item = document.getElementById("item");
const add = document.getElementById("addItem");
const addedItems = document.getElementById("addedItems");
add.onclick = function() {
const itemValue = item.value;
const itemValue2 = ' ';
if (itemValue && itemValue2) {
localStorage.setItem(itemValue, itemValue2);
location.reload();
}
};
for (let a = 0; a < localStorage.length; a++) {
const itemValue = localStorage.key(a);
addedItems.innerHTML += `<div class= 'text'> <div>${itemValue}</div> <div>
<i onclick='check("${itemValue}")' class="fas fa-check"></i><i
onclick='deleteItem("${itemValue}")' class="fas fa-trash-alt"></i>
</div></div> <br>`;
}
function check(itemValue) {
itemValue.classList.add("mystyle");
}
function deleteItem(itemValue) {
localStorage.removeItem(itemValue);
location.reload();
}
function deleteAll() {
localStorage.clear();
location.reload();
}
You can try this answer:
<script>
const item = document.getElementById("item");
const add = document.getElementById("addItem");
const addedItems = document.getElementById("addedItems");
add.onclick = function() {
const itemValue = item.value;
const itemValue2 = ' ';
if (itemValue && itemValue2) {
localStorage.setItem(itemValue, itemValue2);
location.reload();
}
};
for (let a = 0; a < localStorage.length; a++) {
const itemValue = localStorage.key(a);
addedItems.innerHTML += `<div class= 'text'> <div>${itemValue}</div> <div>
<i onclick='check("${itemValue}", ${a})' class="fas fa-check" id="${a}"></i><i
onclick='deleteItem("${itemValue}")' class="fas fa-trash-alt"></i>
</div></div> <br>`;
}
function check(itemValue, id) {
const elem = document.getElementById(id);
elem.classList.add("mystyle");
}
function deleteItem(itemValue) {
localStorage.removeItem(itemValue);
location.reload();
}
function deleteAll() {
localStorage.clear();
location.reload();
}
</script>
<div class="main">
<input id="item" type="text">
<i id="addItem" class="fas fa-plus"></i><br> <br>
</div>
<div class="items">
<div id="addedItems"></div>
</div>
<i onclick='deleteAll()' class="fas fa-trash"></i>
Only changes I made is pass id of element for checkbox click along with itemValue element. Value you are passing checkbox value not document element. If you console you will get to know itemValue is just string not DOM element.
Here is fiddle : https://jsfiddle.net/aviboy2006/ortqu1ps/
Index.html
I'm trying to read data from object but i found that it is not working properly . seekbar also giving null error . The images and song is stored locally and i'm trying to access that.
<div class="music-player">
<div class="song">
<div class="current-song-image"></div>
<div class="current-song-name"><Song Name></div>
<div class="current-song-artist">Song Artist Name</div>
</div>
<!-- song playing bar -->
<div class="song-seek-line">
<div class="curr-time">00:00</div>
<input
type="range"
min="1"
max="100"
value="0"
class="seek-bar"
onchange="jumpTo()"
/>
<div class="end-time">00:00</div>
</div>
<!-- volume control bar -->
<div class="volume-control">
<i class="fa fa-volume-down volume-down"></i>
<input
type="range"
min="1"
max="100"
value="99"
class="volume-bar"
onchange="volume()"
/>
<i class="fa fa-volume-up volume-up"></i>
</div>
<!-- song control buttons -->
<div class="playing-buttons">
<div class="prev-song-button" onclick="prevSong()">
<i class="fa fa-step-backward fa-2x"></i>
</div>
<div class="song-play-pause-button" onclick="PlayPauseSong()">
<i class="fa fa-play-circle fa-5x"></i>
</div>
<div class="next-song-button" onclick="nextSong()">
<i class="fa fa-step-forward fa-2x"></i>
</div>
</div>
</div>
Index.js
let song_num = 0;
let currentlySongPlaying = false;
let changeTimer;
let songlist = [
{
name: "Khaab",
artist:"Akhil",
image: 'images/khaab_song_image.jpg',
songPath: "songs/khaab.mp3"
},
];
// console.log(songlist[2]); // object created successfully
function loadSong(song_num){
clearInterval(changeTimer);
resetValues();
document.createElement('audio').src = songlist[song_num].songPath;
document.createElement('audio').load();
console.log(songlist[song_num].image);
console.log(songlist[song_num].name);
console.log(songlist[song_num].artist);
document.querySelector('.current-song-image').style.backgoundImage = "url(" + songlist[song_num].image + ")";
document.querySelector('.current-song-name').textContent = songlist[song_num].name;
document.querySelector('.current-song-artist').textContent = songlist[song_num].artist;
changeTimer = setInterval(seekUpdatedTime,1000);
document.createElement('audio').addEventListener("songFinished",nextSong);
}
function resetValues(){
document.querySelector('.curr-time').textContent = "00:00";
document.querySelector('.end-time').textContent = "00:00";
document.querySelector('.seek-bar').value = 0;
}
function PlayPauseSong(){
if(!currentlySongPlaying) playSong();
else pauseSong();
}
function playSong(){
document.createElement('audio').play();
currentlySongPlaying = true;
document.querySelector('.song-play-pause-button').innerHTML = '<i class="fa fa-pause-circle fa-5x"></i>';
}
function pauseSong(){
document.createElement('audio').pause();
currentlySongPlaying = false;
document.querySelector('.song-play-pause-button').innerHTML = '<i class="fa fa-play-circle fa-5x"></i>';
}
function nextSong(){
if(song_num<songlist.length-1){
song_num+=1;
}else{
song_num=0;
}
loadSong(song_num);
playSong();
}
function prevSong(){
if(song_num>0){
song_num-=1;
}else{
song_num = songlist.length-1;
}
loadSong(song_num);
playSong();
}
function jumpTo(){
jumpto = document.createElement('audio').duration * (document.querySelector('.seek-bar').value / 100);
document.createElement('audio').currentTime = jumpto;
}
function volume(){
document.createElement('audio').volume = document.querySelector('.volume-bar').value/100;
}
function seekUpdatedTime(){
let seekPosition = 0;
if(!isNaN(document.createElement('audio').duration)){
seekPosition = document.createElement('audio').currentTime*(100/document.createElement('audio').duration);
document.querySelector('.seek-bar').value = seekPosition;
let currentSongMinLeft = Math.floor(document.createElement('audio').currentTime/60);
let currentSongSecLeft = Math.floor(document.createElement('audio').currentTime - currentSongMinLeft * 60);
let SongDurationMin = Math.floor(document.createElement('audio').duration/60);
let SongDurationSec = Math.floor(document.createElement('audio').duration - SongDurationMin * 60);
if(currentSongMinLeft<10)
currentSongMinLeft = "0"+currentSongMinLeft;
if(currentSongSecLeft<10)
currentSongSecLeft = "0"+currentSongSecLeft;
if(SongDurationMin<10)
SongDurationMin = "0"+SongDurationMin;
if(SongDurationSec<10)
SongDurationSec = "0"+SongDurationSec;
currSongTime.textContent = currentSongMinLeft+":"+currentSongSecLeft;
currSongEndTime.textContent = SongDurationMin+":"+SongDurationSec;
}
}
loadSong(song_num);
I'm getting errors like uncaught typeerror queryselector is null in index.js in resetValues() function .
I tried every possible thing..but not getting it. can anyone help .
Try running your code in onload event:
window.onload = function()
loadSong(song_num);
};
or put your code at the end of <body> to ensure it runs after elements are created.
Sorry for the long post, but I tried explaining things in as much detail as possible.
So as I dive deeper into JavaScript and start learning more and more about AJAX requests and other components, I've stumbled across something that I can't seem to figure out.
So below, I will explain what I'm doing and what I would like to do, and see if someone has some guidance for me.
So here is my Vue.js app:
new Vue({
name: 'o365-edit-modal',
el: '#o365-modal-edit',
data: function() {
return {
list: {},
}
},
created() {
this.fetchApplicationsMenu();
},
methods: {
fetchApplicationsMenu() {
var self = this;
wp.apiRequest( {
path: 'fh/v1/menus/applications',
method: 'GET',
}).then(menu => self.list = menu.data);
},
changed() {
const selected = this.$data.list.selected;
function get_ids(list, field) {
const output = [];
for (let i=0; i < list.length ; ++i)
output.push(list[i][field]);
return output;
}
const result = get_ids(selected, "id");
wp.apiRequest( {
path: 'fh/v1/menus/applications',
method: 'PUT',
data: {
ids: result,
},
}).then((post) => {
return post;
},
(error) => {
console.log(error);
});
},
add(x) {
this.$data.list.selected.push(...this.$data.list.available.splice(x, 1));
this.changed();
},
remove(x) {
this.$data.list.available.push(...this.$data.list.selected.splice(x, 1));
this.changed();
},
},
});
Then here is the HTML portion that I'm using to render the two columns:
<div class="column is-half-desktop is-full-mobile buttons">
<nav class="level is-mobile mb-0">
<div class="level-left">
<div class="level-item is-size-5 has-text-left">Selected</div>
</div>
<div class="level-right">
<div class="level-item">
<i class="fas fa-sort-alpha-up is-clickable"></i>
</div>
</div>
</nav>
<hr class="mt-1 mb-3">
<draggable class="list-group"
v-model="list.selected"
v-bind="dragOptions"
:list="list.selected"
:move="onMove"
#change="changed">
<button class="button is-fullwidth is-flex list-group-item o365_app_handle level is-mobile" v-for="(app, index) in list.selected" :key="app.id">
<div class="level-left">
<span class="icon" aria-hidden="true">
<img :src="app.icon_url" />
</span>
<span>{{app.name}}</span>
</div>
<div class="level-right">
<span class="icon has-text-danger is-clickable" #click="remove(index)">
<i class="fas fa-times"></i>
</span>
</div>
</button>
</draggable>
</div>
<div class="column is-half-desktop is-full-mobile buttons">
<div class="is-size-5 has-text-left">Available</div>
<hr class="mt-1 mb-3">
<draggable class="list-group"
v-model="list.available"
v-bind="dragOptions"
:list="list.available"
:move="onMove">
<button class="button is-fullwidth is-flex list-group-item o365_app_handle level is-mobile" v-for="(app, index) in list.available" :key="app.id">
<div class="level-left">
<span class="icon" aria-hidden="true">
<img :src="app.icon_url" />
</span>
<span>{{app.name}}</span>
</div>
<div class="level-right">
<span class="icon has-text-primary is-clickable" #click="add(index)">
<i class="fas fa-plus"></i>
</span>
</div>
</button>
</draggable>
</div>
That outputs the following items, and all works great. See the video display below of each component working as needed. This all works great! I'm calling the changed() method on add and remove which grabs all the IDs and stores them in the DB via an endpoint.
The Problem:
Now I have the following dropdown menu, which depends on the fh/v1/menus/applications endpoint to pull in all the items as shown below:
As you can see below, when I open the dropdown, it has three apps, when I open the cog wheel and remove one of the apps and it saves it but the dropdown doesn't get automatically updated, I have to refresh the page and then I will see the updates.
Does anyone know how to fetch the new items without a refresh?
Here is the HTML and the JS for the dropdown piece:
HTML: As you can see in there, I have data-source="applications" which pulls in the items inside the init_menu as shown in the JS.
<div class="dropdown-menu" id="dropdown-o365" role="menu">
<div class="dropdown-content">
<div class="container is-fluid px-4 pb-4">
<?php if ($application = Applications::init()): ?>
<div class="columns">
<div class="dropdown-item column is-full has-text-centered is-size-6">
<div class="level is-mobile">
<div class="level-left">
<?= $application->get_name() ?>
</div>
<div class="level-right">
<a class="navbar-item modal-element icon" id="o365-apps-cogwheel" data-target="o365-modal-edit" aria-haspopup="true">
<i class="fa fa-cog"></i>
</a>
</div>
</div>
</div>
</div>
<div class="columns is-multiline" data-source="applications"></div>
<?php else: ?>
<div class="columns">
<div class="column is-full">
No applications present.
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
Then here is the JavaScript. I initilize the method inside DOMContentLoaded using init_menu('applications');:
function init_menu(paths)
{
paths.forEach(path => {
const target = document.querySelector('[data-source=' + path + ']');
if (target) {
wp.api.loadPromise.done(function () {
const Menus = wp.api.models.Post.extend({
url: wpApiSettings.root + 'fh/v1/menus/' + path,
});
const menus = new Menus();
menus.fetch().then(posts => {
// This returns the data object.
const data = posts.data;
let post_list;
// Check if it's an array and see if selected is empty otherwise show available.
if (Array.isArray(data.selected) && data.selected.length !== 0) {
post_list = data.selected;
} else {
post_list = data.available;
}
post_list.forEach(function (post) {
switch(path) {
case 'applications':
target.appendChild(create_apps_dom_tree(post));
break;
default:
console.log('Path route is invalid.');
break;
}
})
})
})
}
});
}
function create_apps_dom_tree(post) {
const {
icon_url,
url,
name,
} = post
const container = document.createElement('div');
container.className = 'column is-one-third is-flex py-0';
const anchor = document.createElement('a');
anchor.href = url;
anchor.className = 'dropdown-item px-2 is-flex is-align-items-center';
const figure = document.createElement('figure');
figure.className = 'image is-32x32 is-flex';
const img = document.createElement('img');
img.src = icon_url;
const span = document.createElement('span');
span.className = 'pl-2';
span.textContent = name;
figure.appendChild(img);
anchor.append(figure, span);
container.appendChild(anchor);
return container;
}
If anyone has some guidance or an answer on how to pull in live data from the database on the fly, that would be amazing.
Basically, I need my data-source: to automatically grab the items when my vue/db request is sent so I don't have to refresh the page.
Inside my Vue app, I have the following method:
fetchApplicationsMenu() {
var self = this;
wp.apiRequest( {
path: 'fh/v1/menus/applications',
method: 'GET',
}).then(menu => self.list = menu.data);
},
which calls a GET request and then stores the data inside the return { list: {} }.
A quick fix might be to just invoke init_menu() from the component's beforeDestroy() hook, called when the dialog closes. You might choose to do it from changed() instead if the dropdown is still accessible with this dialog open.
new Vue({
// option 1:
beforeDestroy() {
init_menu('applications');
},
// option 2:
methods: {
changed() {
init_menu('applications');
}
}
})
Alternative: You already know what the final application list is in changed(), so you could update the dropdown with the new list from that method.
function update_menu(path, post_list) {
const target = document.querySelector('[data-source=' + path + ']');
// remove all existing children
Array.from(target.childNodes).forEach(x => x.remove());
post_list.forEach(post => target.appendChild(create_apps_dom_tree(post)))
}
new Vue({
methods: {
changed() {
update_menu('applications', this.$data.available);
}
}
})