Why does nesting my divs break document.getElementById()? - javascript

I'm having an issue while designing a music player. My music player works with the following code:
playlist_index = 0;
// Set object references
playbtn = document.getElementById("playpausebtn");
seekslider = document.getElementById("seekslider");
volumeslider = document.getElementById("volumeslider");
curtimetext = document.getElementById("curtimetext");
durtimetext = document.getElementById("durtimetext");
playlist_status = document.getElementById("playlist_status");
playerImg = document.getElementById("playerImg");
// Audio Object
audio = new Audio();
audio.src = dir+playlist[0].song_url;
audio.loop = false;
playlist_status.innerHTML = playlist[playlist_index].song_name + '-' + playlist[playlist_index].band_name;
// Add Event Handling
playbtn.addEventListener("click",playPause);
mutebtn.addEventListener("click", mute);
seekslider.addEventListener("mousedown", function(event){ seeking=true; seek(event); });
seekslider.addEventListener("mousemove", function(event){ seek(event); });
seekslider.addEventListener("mouseup",function(){ seeking=false; });
volumeslider.addEventListener("mousemove", setvolume);
audio.addEventListener("timeupdate", function(){ seektimeupdate(); });
audio.addEventListener("ended", function(){ switchTrack(); });
audio.addEventListener("play", function(){ playbtn.style.background = "url(http://localhost/wordpress/wp-content/themes/virtue-child/pause.png) no-repeat"; });
// Functions
function switchTrack(){
if(playlist_index == (playlist.length - 1)){
playlist_index = 0;
} else {
playlist_index++;
}
playlist_status.innerHTML = playlist[playlist_index].song_name + '-' + playlist[playlist_index].band_name;
audio.src = dir+playlist[playlist_index].song_url;
setTimeout(audio.play(), 2000);
}
(plus some more functions for updating seek bars and times) and the following HTML:
<div id="audio_player">
<div id="playerImg"></div>
<div id="audio_controls">
<button id="playpausebtn"></button>
<p id="playlist_status"></p>
<div id="sliderHolder">
<input id="seekslider" type="range" min="0" max="100" value="0" step="1">
<span id="curtimetext">00:00</span>
<span id="durtimetext">00:00</span>
</div>
<button id="mutebtn"></button>
<br/>
<input id="volumeslider" type="range" min="0" max="100" value="100" step="1">
</div>
</div>
But when I start housing elements in container divs, the above code fails to update the time and the seek slider doesn't work. The housed and styled audio player HTML looks like this:
<div id="audio_player">
<img src="wp-content/uploads/band-photos/Logo.png" id="playerImg">
<p id="playlist_status"></p>
<div id="audio_controls">
<div id="sliderHolder">
<input id="seekslider" type="range" min="0" max="100" value="0" step="1">
<span id="curtimetext">00:00</span>
<span id="durtimetext">00:00</span>
</div>
<div id="musicButtons">
<button id="playpausebtn"></button>
<div id="volumeHolder">
<img src="http://localhost/wordpress/wp-content/themes/virtue-child/speaker.png" id="volButton">
<input id="volumeslider" type="range" min="0" max="100" value="100" step="1">
</div>
</div>
</div>
</div>
Is there an issue when I begin to house elements in divs and searching the DOM for them? Again, the JavaScript works with the first set of HTML, not the second.

Thank you everyone for the input. It looks like in styling the new player I removed the mute button. Therefore: 'mutebtn.addEventListener("click", mute);' was throwing an error and causing the rest of the functions not to work.

Related

Multiple Javascript functions embedded in html

I am trying to implement two sliders in HTML and use Javascript functions to update the indicator of the values of those sliders. I don't know how to structure the code for the output of each slider. I think there is a problem with the way that the Javascript codes are embedded. Does anyone know how I can solve this issue?
Purpose: Have two sliders with two separate indicators in HTML
Thanks!
<body>
<h1>Round Range Slider</h1>
<div class="slidecontainer">
<input type="range" min="1" max="100" value="50" class="slider" id="myRange">
<p>Value: <span id="demo"></span></p>
</div>
<div class="slidecontainer">
<input type="range" min="1" max="100" value="50" class="slider" id="myRange1">
<p>Value1: <span id="demo1"></span></p>
</div>
</body>
<head>
<script>
var slider = document.getElementById("myRange");
var output = document.getElementById("demo");
output.innerHTML = slider.value;
slider.oninput = function() {
output.innerHTML = this.value;
}
</script>
<script>
var slider = document.getElementById("myRange1");
var output = document.getElementById("demo1");
output.innerHTML = slider.value;
slider.oninput = function() {
output.innerHTML = this.value;
}
</script>
</head>
Duplicating the code, taking into account the introduction of small changes - is bad.
I made you a js code with the forEach() method. This means that now you can control many input without having to write js logic for every.
Just replace your js code with this one:
let input = document.querySelectorAll('.slidecontainer input');
let result = document.querySelectorAll('.slidecontainer span');
input.forEach(function(input_current, index) {
input_current.oninput = function() {
result[index].innerHTML = this.value;
}
});
Declaring variables in separate script tags does not create two copies of the variable. You can declare a variable for each slider by giving them different names.
<body>
<h1>Round Range Slider</h1>
<div class="slidecontainer">
<input type="range" min="1" max="100" value="50"
class="slider" id="myRange">
<p>Value: <span id="demo"></span></p>
</div>
<div class="slidecontainer">
<input type="range" min="1" max="100" value="50"
class="slider" id="myRange1">
<p>Value1: <span id="demo1"></span></p>
</div>
</body>
<head>
<script>
var slider = document.getElementById("myRange");
var output = document.getElementById("demo");
output.innerHTML = slider.value;
slider.oninput = function() {
output.innerHTML = this.value;
}
var slider1 = document.getElementById("myRange1");
var output1 = document.getElementById("demo1");
output1.innerHTML = slider1.value;
slider1.oninput = function() {
output1.innerHTML = this.value;
}
</script>
</head>
Better yet, use a loop:
<body>
<h1>Round Range Slider</h1>
<div class="slidecontainer">
<input type="range" min="1" max="100" value="50"
class="slider" id="myRange">
<p>Value: <span id="demo"></span></p>
</div>
<div class="slidecontainer">
<input type="range" min="1" max="100" value="50"
class="slider" id="myRange1">
<p>Value1: <span id="demo1"></span></p>
</div>
</body>
<head>
<script>
let sliders = document.querySelectorAll(".slidecontainer");
sliders.forEach(slideContainer => {
// Get a reference to the children of the current container.
let sliderChild = slideContainer.children[0];
let spanChild = slideContainer.children[1].children[0];
// Attach an event listener to each slider.
sliderChild.oninput = () => spanChild.innerText = sliderChild.value;
// Initialize the label.
spanChild.innerText = sliderChild.value;
});
</script>
</head>

JavaScript to output html range slider values for multiple sliders

I want to create two range sliders in html and display their current value with a JS script below each slider. But in my code both sliders will output their value to the same "value output" (the one for the slider with id="myRange2"). Why does this happen and how can I fix this?
I tried to make the slider and output variables unique for each slider script, but that does not solve my problem.
This issue is similar, but I am not so familiar with JS and I think my solution might be more simple.
This is my .html code with the JS scripts at the end:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div class="slidecontainer">
<input type="range" min="1" max="100" value="50" class="slider" id="myRange">
<p>Value: <span id="demo"></span></p>
</div>
<div class="slidecontainer">
<input type="range" min="1" max="100" value="50" class="slider" id="myRange2">
<p>Value: <span id="demo2"></span></p>
</div>
<script>
var slider = document.getElementById("myRange");
var output = document.getElementById("demo");
output.innerHTML = slider.value;
slider.oninput = function() {
output.innerHTML = this.value;
}
</script>
<script>
var slider = document.getElementById("myRange2");
var output = document.getElementById("demo2");
output.innerHTML = slider.value;
slider.oninput = function() {
output.innerHTML = this.value;
}
</script>
</body>
</html>
You have to use different names for variables
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div class="slidecontainer">
<input type="range" min="1" max="100" value="50" class="slider" id="myRange">
<p>Value: <span id="demo"></span></p>
</div>
<div class="slidecontainer">
<input type="range" min="1" max="100" value="50" class="slider" id="myRange2">
<p>Value: <span id="demo2"></span></p>
</div>
<script>
var slider1 = document.getElementById("myRange");
var demo = document.getElementById("demo");
demo.innerHTML = slider1.value;
slider1.oninput = function() {
demo.innerHTML = this.value;
}
</script>
<script>
var slider2 = document.getElementById("myRange2");
var output = document.getElementById("demo2");
output.innerHTML = slider2.value;
slider2.oninput = function() {
output.innerHTML = this.value;
}
</script>
</body>
</html>

Display values of multiple input type range in one page

I'm trying to display values of every slider I have on my page, this is my code so far:
var i = 0;
var st = 'slider';
var ot = 'output';
var s = '';
var o = '';
for (var x = 0; x < 3; x++) {
i++;
s = st+i;
o = ot+i;
var s = document.getElementById("range"+i);
var o = document.getElementById("limit"+i);
o.innerHTML = s.value;
s.oninput = function() {
o.innerHTML = this.value;
}
}
<div id="slidecontainer">
<input type="range" min="2" max="50" value="20" class="slider" id="range1" >
<label>You chose <span id="limit1"></span></label>
</div>
<div id="slidecontainer">
<input type="range" min="2" max="50" value="20" class="slider" id="range2" >
<label>You chose <span id="limit2"></span></label>
</div>
<div id="slidecontainer">
<input type="range" min="2" max="50" value="20" class="slider" id="range3" >
<label>You chose <span id="limit3"></span></label>
</div>
It's only changing the last value when I move any slider, I want to display the value of each slider respectively. I'm using a loop in my JavaScript code because I have more than 20 sliders and I don't want to write a function for each of them unless that is the only way of doing it. Any suggestions?
The problem you are having is related to variable scope. There is only one variable named o, each iteration of the loop changes this variable. So when the
oninput function is evaluated o equals the last value you set it to equal. The current value of o is not "saved" in the function definition.
See https://www.w3schools.com/js/js_scope.asp for more information.
See solution below, here I find the limit in each call to the function.
function updateLabel() {
var limit = this.parentElement.getElementsByClassName("limit")[0];
limit.innerHTML = this.value;
}
var slideContainers = document.getElementsByClassName("slidecontainer");
for (var i = 0; i < slideContainers.length; i++) {
var slider = slideContainers[i].getElementsByClassName("slider")[0];
updateLabel.call(slider);
slider.oninput = updateLabel;
}
<div class="slidecontainer">
<input type="range" min="2" max="50" value="20" class="slider">
<label>You chose <span class="limit"></span></label>
</div>
<div class="slidecontainer">
<input type="range" min="2" max="50" value="20" class="slider">
<label>You chose <span class="limit"></span></label>
</div>
<div class="slidecontainer">
<input type="range" min="2" max="50" value="20" class="slider">
<label>You chose <span class="limit"></span></label>
</div>

One JS function to handle output from 3 sliders

I am unsuccessfully trying to squeze the first three functions into one, so that one function can handle different outputs from three different sliders instead of creating the same functions 3 times for different elements. Is there a good way to do it?
var x = document.getElementById("fader1");
x.addEventListener("mousemove", function(input){
document.getElementById("protein").value = x.value;
});
var y = document.getElementById("fader2");
y.addEventListener("mousemove", function(input){
document.getElementById("carbs").value = y.value;
});
var z = document.getElementById("fader3");
z.addEventListener("mousemove", function(input){
document.getElementById("fat").value = z.value;
});
$(function(){
$(".bar").on("input", function () {
var val1 = $("#fader1").val();
var val2 = $("#fader2").val();
var val3 = $("#fader3").val();
var sum = (val1*4) + (val2*4) + (val3*9);
$("#sum").text(sum);
switch (true) {
case (sum <= 600):
$("#sum").css("color", "red");
break;
case (sum > 601 && sum <= 1200):
$("#sum").css("color", "yellow");
break;
case (sum > 1200):
$("#sum").css("color", "orange");
break;
default:
$("#sum").css("color", "red");
};
});
});
<body>
<div class="container">
<div class="protein">
<p>Protein</p>
<output id="protein"></output>
<input type="range" min="0" max="100" value="" id="fader1" class="bar">
</div>
<div class="carbs">
<p>Carbohydrates</p>
<output id="carbs"></output>
<input type="range" min="0" max="100" value="" id="fader2" class="bar">
</div>
<div class="fat">
<p>Fat</p>
<output id="fat"></output>
<input type="range" min="0" max="100" value="" id="fader3" class="bar">
</div>
<div class="total">
<p>Calories: </p>
<div id="sum">
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
</body>
Just use the input event and traverse to the current <output> using prev(). This also allows you to trigger the input event on page load to fill in the text values.
Using another event on top of the input event doesn't make sense
$(".bar").on("input", function() {
// "this" is current input event occurs on - set output value accordingly
$(this).prev().val($(this).val());
var val1 = $("#fader1").val();
var val2 = $("#fader2").val();
var val3 = $("#fader3").val();
var sum = (val1 * 4) + (val2 * 4) + (val3 * 9);
$("#sum").text(sum);
// css switch removed for clarity
// now trigger the event on page load
}).trigger('input');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="protein">
<p>Protein</p>
<output id="protein"></output>
<input type="range" min="0" max="100" value="" id="fader1" class="bar">
</div>
<div class="carbs">
<p>Carbohydrates</p>
<output id="carbs"></output>
<input type="range" min="0" max="100" value="" id="fader2" class="bar">
</div>
<div class="fat">
<p>Fat</p>
<output id="fat"></output>
<input type="range" min="0" max="100" value="" id="fader3" class="bar">
</div>
<div class="total">
<p>Calories: </p>
<div id="sum">
</div>
</div>
</div>
<body>
<div class="container">
<div class="protein">
<p>Protein</p>
<output id="protein"></output>
<input type="range" min="0" max="100" value="" id="fader1" class="bar">
</div>
<div class="carbs">
<p>Carbohydrates</p>
<output id="carbs"></output>
<input type="range" min="0" max="100" value="" id="fader2" class="bar">
</div>
<div class="fat">
<p>Fat</p>
<output id="fat"></output>
<input type="range" min="0" max="100" value="" id="fader3" class="bar">
</div>
<div class="total">
<p>Calories: </p>
<div id="sum">
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
$('input').mousemove(function(){
$(this).siblings('output').val($(this).val());
})
$(function(){
$(".bar").on("input", function () {
var valueElements = $('input');
var sum = (valueElements[0].value *4) + (valueElements[1].value*4) + (valueElements[2].value*9);
$("#sum").text(sum);
switch (true) {
case (sum <= 600):
$("#sum").css("color", "red");
break;
case (sum > 601 && sum <= 1200):
$("#sum").css("color", "yellow");
break;
case (sum > 1200):
$("#sum").css("color", "orange");
break;
default:
$("#sum").css("color", "red");
};
});
});
</script>
</body>
Yes there is, create a function and call it with different ids, like below using previousSibling
setValues('fader1');
setValues('fader2');
setValues('fader3');
// our function for setting values
function setValues(id) {
var x = document.getElementById(id);
x.addEventListener("mousemove", function(){
x.previousElementSibling.innerHTML = x.value;
});
}
setValues('fader1');
setValues('fader2');
setValues('fader3');
$(function(){
$(".bar").on("input", function () {
var val1 = $("#fader1").val();
var val2 = $("#fader2").val();
var val3 = $("#fader3").val();
var sum = (val1*4) + (val2*4) + (val3*9);
$("#sum").text(sum);
switch (true) {
case (sum <= 600):
$("#sum").css("color", "red");
break;
case (sum > 601 && sum <= 1200):
$("#sum").css("color", "yellow");
break;
case (sum > 1200):
$("#sum").css("color", "orange");
break;
default:
$("#sum").css("color", "red");
};
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="protein">
<p>Protein</p>
<output id="protein"></output>
<input type="range" min="0" max="100" value="" id="fader1" class="bar">
</div>
<div class="carbs">
<p>Carbohydrates</p>
<output id="carbs"></output>
<input type="range" min="0" max="100" value="" id="fader2" class="bar">
</div>
<div class="fat">
<p>Fat</p>
<output id="fat"></output>
<input type="range" min="0" max="100" value="" id="fader3" class="bar">
</div>
<div class="total">
<p>Calories: </p>
<div id="sum">
</div>
</div>
</div>

Volume controls and buffering issues with Web Audio API

I'm running into some issues writing an app using the Web Audio API.
I'm trying to play multiple sounds with Web Audio API, each with an individual volume control and able to have multiple different sounds playing at once., but I can't figure out how to implement it.
I can figure out how to implement a universal volume control, but would like to be able to increase/decrease individual sounds. I am also having the issue that when I have multiple sounds playing, I can't seem to stop them all.. one sounds seems to always get stuck somewhere.
Any advise on how to update the below code would be greatly appreciated.
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="css/loopy_styles.css" />
<script>
context = new (window.AudioContext || window.webkitAudioContext)();
//We set up an array of buffers to store our sounds in, with an extra entry as dummy '0' entry
var soundBuffers = [null,null,null,null,null,null];
//We load our sounds into the buffer when the page is opened.
window.onload = function() {
loadSound('sounds/fire_test1.wav', 1);
loadSound('sounds/wind_test.wav', 2);
loadSound('sounds/rain_test1.wav', 3);
loadSound('sounds/stream.mp3', 4);
loadSound('sounds/spring_test.wav', 5);
};
var currentlyPlayingSoundNum = 0;
var currentlyPlayingSound = null;
//Function to play sound, take in a number to represent each sound icon.
function play_sound(num){
if (num) {
//Shows the paused version of button, hides the play version of the button.
document.getElementById('playBtn'+num+'_play').style.display = 'none';
document.getElementById('playBtn'+num+'_stop').style.display = 'block';
//Sets the currently playing sound to the number supplied to the function.
currentlyPlayingSoundNum = num;
//Creates buffer for sound.
currentlyPlayingSound = context.createBufferSource();
//Sets the sound to loop continuously so we have seemless play-back.
currentlyPlayingSound.looping = true;
//Sends the sound at spot num in our buffer array to the buffer for our currently playing sound.
currentlyPlayingSound.buffer = soundBuffers[num];
currentlyPlayingSound.connect(context.destination);
//Start playing the sound.
currentlyPlayingSound.start(0);
}
}
//Function to stop sound, take in a number to represent each sound icon.
function stop_sound(num){
if(num){
document.getElementById('playBtn'+num+'_play').style.display = 'block';
document.getElementById('playBtn'+num+'_stop').style.display = 'none';
}
if (currentlyPlayingSound) {
//Shows the play version of button, hides the paused version of the button.
document.getElementById('playBtn'+currentlyPlayingSoundNum+'_play').style.display = 'block';
document.getElementById('playBtn'+currentlyPlayingSoundNum+'_stop').style.display = 'none';
//Stops playing the currently playing sound.
currentlyPlayingSound.stop(0);
currentlyPlayingSound = null;
currentlyPlayingSoundNumber = 0;
}
}
function loadSound(url, bufferNum) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function() {
var successCallback = function(buffer) {
soundBuffers[bufferNum] = buffer;
}
var errorCallback = function(e) {
console.log(e);
}
context.decodeAudioData(request.response, successCallback, errorCallback);
}
request.send();
}
</script>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<title>Ambient Sound Generator</title>
</head>
<body>
<div class="background">
<div id="intro-text">
<h1 id="site-title">Ambient Sound Generator</h1>
<p>Mix ambient sounds together to block out distractions and help you focus or relax</p>
<p>Click the buttons below to begin</p>
</div>
<div id="button-container">
<div id="btn1">
<input type="image" class="pp_img" src="img/fire-pause.png" name="Fire" id="playBtn1_play" onclick="play_sound(1);" />
<input type="image" class="pp_img" src="img/fire-play.png" name="Fire" id="playBtn1_stop" onclick="stop_sound(1);" style="display:none" />
<p><input type="range" min="0" max="100" value="100" onchange="sample.changeVolume();">Volume</p>
</div>
<div id="btn2">
<input type="image" class="pp_img" src="img/wind-pause.png" name="Wind" id="playBtn2_play" onclick="play_sound(2);" />
<input type="image" class="pp_img" src="img/wind-play.png" name="Wind" id="playBtn2_stop" onclick="stop_sound(2);" style="display:none" />
<p><input type="range" min="0" max="100" value="100" onchange="sample.changeVolume();">Volume</p>
</div>
<div id="btn3">
<input type="image" class="pp_img" src="img/rain-pause.png" name="Rain" id="playBtn3_play" onclick="play_sound(3);"/>
<input type="image" class="pp_img" src="img/rain-play.png" name="Rain" id="playBtn3_stop" onclick="stop_sound(3);" style="display:none"/>
<p><input type="range" min="0" max="100" value="100" onchange="sample.changeVolume();">Volume</p>
</div>
<div id="btn4">
<input type="image" class="pp_img" src="img/stream-pause.png" name="Stream" id="playBtn4_play" onclick="play_sound(4);"/>
<input type="image" class="pp_img" src="img/stream-play.png" name="Stream" id="playBtn4_stop" onclick="stop_sound(4);" style="display:none"/>
<p><input type="range" min="0" max="100" value="100" onchange="sample.changeVolume();">Volume</p>
</div>
<div id="btn5">
<input type="image" class="pp_img" src="img/forest-pause.png" name="Rain" id="playBtn5_play" onclick="play_sound(5);"/>
<input type="image" class="pp_img" src="img/forest-play.png" name="Rain" id="playBtn5_stop" onclick="stop_sound(5);" style="display:none" />
<p><input type="range" min="0" max="100" value="100" onchange="sample.changeVolume();">Volume</p>
</div>
</div>
</div>
</body>
<script>
function refreshData(){
x = 1; // x = seconds
var d = new Date()
var h = d.getHours();
var m = d.getMinutes();
var s = d.getSeconds();
if (h<=9) {h = '0'+h};
if (m<=9) {m = '0'+m};
if (s<=9) {s = '0'+s};
var color = '#'+h+m+s;
$("div.background").css("background-color", color );
$("p#hex").text(color);
setTimeout(refreshData, x*1000);
}
refreshData(); // execute function
</script>
Instead of
currentlyPlayingSound.connect(context.destination);
You just need to create another GainNode and chain it in:
var gain = context.createGain();
gain.gain.value = /*insert your gain variable here */;
currentlyPlayingSound.connect(gain);
gain.connect(context.destination);
You'll probably want to cache the gain node somewhere (so you can have a slider, etc.)

Categories

Resources