Web audio oscillator is clicking in Firefox only - javascript
I'm trying to create a simple metronome using the web audio oscillator, so that no external audio files are needed. I'm creating the sound of the metronome by ramping the volume of the oscillator up and down very quickly (since you can't use start() and stop() more than once), and then repeating that function at a set interval. It ends up sounding like a nice little wood block.
The code below works/sounds great in Chrome, Safari and Opera. But in Firefox, there's a nasty intermittent "click" when the volume ramps up. I've tried changing the attack/release times to get rid of the click, but they have to be really, really long before it consistently disappears. So long, in fact, that the oscillator just sounds like a sustained note.
var audio = new (window.AudioContext || window.webkitAudioContext)();
var tick = audio.createOscillator();
var tickVol = audio.createGain();
tick.type = 'sine';
tick.frequency.value = 1000;
tickVol.gain.value = 0; //setting the volume to 0 before I connect everything
tick.connect(tickVol);
tickVol.connect(audio.destination);
tick.start(0);
var metronome = {
start: function repeat() {
now = audio.currentTime;
//Make sure volume is 0 and that no events are changing it
tickVol.gain.cancelScheduledValues(now);
tickVol.gain.setValueAtTime(0, now);
//Play the osc with a super fast attack and release so it sounds like a click
tickVol.gain.linearRampToValueAtTime(1, now + .001);
tickVol.gain.linearRampToValueAtTime(0, now + .001 + .01);
//Repeat this function every half second
click = setTimeout(repeat, 500);
},
stop: function() {
if(typeof click !== 'undefined') {
clearTimeout(click);
tickVol.gain.value = 0;
}
}
}
$("#start").click(function(){
metronome.start();
});
$("#stop").click(function(){
metronome.stop();
});
Codepen
Is there any way to get FF to sound like the other 3 browsers?
I was getting the exact same problem in latest Opera and found the problem to be the individual sounds 'decimal time length'.
I wrote a morse code translator, and like yours, it's just a series of simple short sounds/beeps created via createOscillator.
With morse code you have a speed count (words per minute) based on a 5 letter long word like codex or paris.
To get 20 or 30 paris' per minute to finish exactly on the minute, I had to use a sound time length of, for example, 0.61. In Opera, this caused the 'end of sound click'. On changing this to 0.6 and the click disappeared across all browsers - except Firefox.
I've tried freq = 0 and gain = 0 between sounds but still get the click at the end in FF and I don't know enough about Web Audio to try anything else.
On another note, I noticed you're using a loop and timeout to get to the next tick. Have you tried an 'Oscillator onended function' instead? I've used it with a simple counter increment and variable length blank sound/note. Go to the very end of my JS if you want to have a look.
**UPDATE - I've been fiddling about with setValueAtTime() and linearRampToValueAtTime() and appeared to have cracked the click problem. Scroll to bottom of script to see example. **
(function(){
/* Morse Code Generator & Translator - Kurt Grigg 2003 (Updated for sound and CSS3) */
var d = document;
d.write('<div class="Mcontainer">'
+'<div class="Mtitle">Morse Code Generator Translator</div>'
+'<textarea id="txt_in" class="Mtxtarea"></textarea>'
+'<div class="Mtxtareatitle">Input</div>'
+'<textarea id="txt_out" class="Mtxtarea" style="top: 131px;"></textarea>'
+'<div class="Mtxtareatitle" style="top: 172px;">Output</div>'
+'<div class="Mbuttonwrap">'
+'<input type="button" class="Mbuttons" id="how" value="!">'
+'<input type="button" class="Mbuttons" id="tra" value="translate">'
+'<input type="button" class="Mbuttons" id="ply" value="play">'
+'<input type="button" class="Mbuttons" id="pau" value="pause">'
+'<input type="button" class="Mbuttons" id="res" value="reset"></div>'
+'<select id="select" class="Mselect">'
+'<option value=0.07 selected="selected">15 wpm</option>'
+'<option value=0.05>20 wpm</option>'
+'<option value=0.03>30 wpm</option>'
+'</select>'
+'<div class="sliderWrap">volume <input id="volume" type="range" min="0" max="1" step="0.01" value="0.05"/></div>'
+'<div class="Mchckboxwrap">'
+'<span style="text-align: right;">separator <input type="checkbox" id="slash" class="Mchckbox"></span>'
+'</div>'
+'<div id="about" class="Minfo">'
+'<b>Input morse</b><br>'
+'<ul><li>Enter morse into input box using full stop (period) and minus sign (hyphen)</li>'
+'<li>Morse letters must be separated by 1 space</li>'
+'<li>Morse words must be separated by 3 or more spaces</li>'
+'<li>You can use / to separate morse words. There must be at least 1 space before and after each separator used</li>'
+'</ul>'
+'<b>Input text</b><br>'
+'<ul class="Mul"><li>Enter text into input box</li>'
+'<li>Characters that cannot be translated will be ignored</li>'
+'<li>If morse and text is entered, the converter will assume morse mode</li></ul>'
+'<input type="button" value="close" id="clo" class="Mbuttons">'
+'</div><div id="mdl" class="modal"><div id="bdy"><div id="modalMsg">A MSG</div><input type="button" value="close" id="cls" class="Mbuttons"></div></div></div>');
var ftmp = d.getElementById('mdl');
var del;
d.getElementById('tra').addEventListener("click", function(){convertToAndFromMorse(txtIn.value);},false);
d.getElementById('ply').addEventListener("click", function(){CancelIfPlaying();},false);
d.getElementById('pau').addEventListener("click", function(){stp();},false);
d.getElementById('res').addEventListener("click", function(){Rst();txtIn.value = '';txtOt.value = '';},false);
d.getElementById('how').addEventListener("click", function(){msgSelect();},false);
d.getElementById('clo').addEventListener("click", function(){fadeOut();},false);
d.getElementById('cls').addEventListener("click", function(){fadeOut();},false);
d.getElementById('bdy').addEventListener("click", function(){errorSelect();},false);
var wpm = d.getElementById('select');
wpm.addEventListener("click", function(){wpMin()},false);
var inc = 0;
var playing = false;
var txtIn = d.getElementById('txt_in');
var txtOt = d.getElementById('txt_out');
var paused = false;
var allowed = ['-','.',' '];
var aud;
var tmp = (window.AudioContext || window.webkitAudioContext)?true:false;
if (tmp) {
aud = new (window.AudioContext || window.webkitAudioContext)();
}
var incr = 0;
var speed = parseFloat(wpm.options[wpm.selectedIndex].value);
var char = [];
var alphabet = [["A",".-"],["B","-..."],["C","-.-."],["D","-.."],["E","."],["F","..-."],["G","--."],["H","...."],["I",".."],["J",".---"],
["K","-.-"],["L",".-.."],["M","--"],["N","-."],["O","---"],["P",".--."],["Q","--.-"],["R",".-."],["S","..."],["T","-"],["U","..-"],
["V","...-"],["W",".--"],["X","-..-"],["Y","-.--"],["Z","--.."],["1",".----"],["2","..---"],["3","...--"],["4","....-"],["5","....."],
["6","-...."],["7","--..."],["8","---.."],["9","----."],["0","-----"],[".",".-.-.-"],[",","--..--"],["?","..--.."],["'",".----."],["!","-.-.--"],
["/","-..-."],[":","---..."],[";","-.-.-."],["=","-...-"],["-","-....-"],["_","..--.-"],["\"",".-..-."],["#",".--.-."],["(","-.--.-"],[" ",""]];
function errorSelect() {
txtIn.focus();
}
function modalSwap(msg) {
d.getElementById('modalMsg').innerHTML = msg;
}
function msgSelect() {
ftmp = d.getElementById('about');
fadeIn();
}
function fadeIn() {
ftmp.removeEventListener("transitionend", freset);
ftmp.style.display = "block";
del = setTimeout(doFadeIn,100);
}
function doFadeIn() {
clearTimeout(del);
ftmp.style.transition = "opacity 0.5s linear";
ftmp.style.opacity = "1";
}
function fadeOut() {
ftmp.style.transition = "opacity 0.8s linear";
ftmp.style.opacity = "0";
ftmp.addEventListener("transitionend",freset , false);
}
function freset() {
ftmp.style.display = "none";
ftmp.style.transition = "";
ftmp = d.getElementById('mdl');
}
function stp() {
paused = true;
}
function wpMin() {
speed = parseFloat(wpm.options[wpm.selectedIndex].value);
}
function Rst(){
char = [];
inc = 0;
playing = false;
paused = false;
}
function CancelIfPlaying(){
if (window.AudioContext || window.webkitAudioContext) {paused = false;
if (!playing) {
IsReadyToHear();
}
else {
return false;
}
}
else {
modalSwap("<p>Your browser doesn't support Web Audio API</p>");
fadeIn();
return false;
}
}
function IsReadyToHear(x){
if (txtIn.value == "" || /^\s+$/.test(txtIn.value)) {
modalSwap('<p>Nothing to play, enter morse or text first</p>');
fadeIn();
txtIn.value = '';
return false;
}
else if (char.length < 1 && (x != "" || !/^\s+$/.test(txtIn.value)) && txtIn.value.length > 0) {
modalSwap('<p>Click Translate button first . . .</p>');
fadeIn();
return false;
}
else{
playMorse();
}
}
function convertToAndFromMorse(x){
var swap = [];
var outPut = "";
x = x.toUpperCase();
/* Is input empty or all whitespace? */
if (x == '' || /^\s+$/.test(x)) {
modalSwap("<p>Nothing to translate, enter morse or text</p>");
fadeIn();
txtIn.value = '';
return false;
}
/* Remove front & end whitespace */
x = x.replace(/\s+$|^\s*/gi, '');
txtIn.value = x;
txtOt.value = "";
var isMorse = (/(\.|\-)\.|(\.|\-)\-/i.test(x));// Good enough.
if (!isMorse){
for (var i = 0; i < alphabet.length; i++){
swap[i] = [];
for (var j = 0; j < 2; j++){
swap[i][j] = alphabet[i][j].replace(/\-/gi, '\\-');
}
}
}
var swtch1 = (isMorse) ? allowed : swap;
var tst = new RegExp( '[^' + swtch1.join('') + ']', 'g' );
var swtch2 = (isMorse)?' ':'';
x = x.replace( tst, swtch2); //remove unwanted chars.
x = x.split(swtch2);
if (isMorse) {
var tidy = [];
for (var i = 0; i < x.length; i++){
if ((x[i] != '') || x[i+1] == '' && x[i+2] != '') {
tidy.push(x[i]);
}
}
}
var swtch3 = (isMorse) ? tidy : x;
for (var j = 0; j < swtch3.length; j++) {
for (var i = 0; i < alphabet.length; i++){
if (isMorse) {
if (tidy[j] == alphabet[i][1]) {
outPut += alphabet[i][0];
}
}
else {
if (x[j] == alphabet[i][0]) {
outPut += alphabet[i][1] + ((j < x.length-1)?" ":"");
}
}
}
}
if (!isMorse) {
var wordDivide = (d.getElementById('slash').checked)?" / ":" ";
outPut = outPut.replace(/\s{3,}/gi, wordDivide);
}
if (outPut.length < 1) {
alert('Enter valid text or morse...');
txtIn.value = '';
}
else {
txtOt.value = outPut;
}
var justMorse = (!isMorse) ? outPut : tidy;
FormatForSound(justMorse);
}
function FormatForSound(s){
var n = [];
var b = '';
if (typeof s == 'object') {
for (var i = 0; i < s.length; ++i) {
var f = (i == s.length-1)?'':' ';
var t = b += (s[i] + f);
}
}
var c = (typeof s == 'object')? t : s;
c = c.replace(/\//gi, '');
c = c.replace(/\s{1,3}/gi, '4');
c = c.replace(/\./gi, '03');
c = c.replace(/\-/gi, '13');
c = c.split('');
for (var i = 0; i < c.length; i++) {
n.push(c[i]);
}
char = n;
}
function vlm() {
return document.getElementById('volume').value;
}
function playMorse() {
if (paused){
playing = false;
return false;
}
playing = true;
if (incr >= char.length) {
incr = 0;
playing = false;
paused = false;
return false;
}
var c = char[incr];
var freq = 550;
var volume = (c < 2) ? vlm() : 0 ;
var flen = (c == 0 || c == 3) ? speed : speed * 3;
var osc = aud.createOscillator();
osc.type = 'sine';
osc.frequency.value = freq;
var oscGain = aud.createGain();
oscGain.gain.value = volume;
osc.connect(oscGain);
oscGain.connect(aud.destination);
var now = aud.currentTime;
osc.start(now);
/*
Sharp volume fade to stop harsh clicks if wave is stopped
at a point other than the (natural zero crossing point)
*/
oscGain.gain.setValueAtTime(volume, now + (flen*0.8));
oscGain.gain.linearRampToValueAtTime(0.0, now + (flen*0.9999));
osc.stop(now + flen);
osc.onended = function() {
incr++;
playMorse();
}
}
})();
body {
text-align: center;
}
.Mcontainer {
display: inline-block;
position: relative;
width: 382px;
height: 302px;
border: 1px solid #000;
border-radius: 6px;
text-align: center;
font: bold 11px sans-serif;
background-color: rgb(203,243,65);
box-shadow: 0px 4px 2px rgba(0,0,0,0.3);
}
.Mtitle {
-webkit-user-select: none;
-moz-user-select: none;
display: inline-block;
position: absolute;
width: 380px;
height: 20px;
margin: auto;
left: 0; right: 0;
font-size: 16px;
line-height: 20px;
color: #666;
}
.Mtxtareatitle {
-webkit-user-select: none;
-moz-user-select: none;
display: block;
position: absolute;
top: 60px;
left: -36px;
height: 22px;
width: 106px;
font-size: 18px;
line-height: 22px;
text-align: center;
color: #555;
transform: rotate(-90deg);
}
.Mtxtarea {
display: block;
position: absolute;
top: 18px;
margin: auto;
left: 0; right: 0;
height: 98px;
width: 344px;
border: 0.5px solid #000;
border-radius: 6px;
padding-top: 6px;
padding-left: 24px;
resize: none;
background-color: #fffff0;
font: bold 10px courier;
color: #555;
text-transform: uppercase;
overflow: auto;
outline: 0; box-shadow: inset 0px 2px 5px rgba(0,0,0,0.5);
}
.Minfo {
display: none;
position: absolute;
top: -6px; left:-6px;
padding: 6px;
height: auto;
width: 370px;
text-align: left;
border: 0.5px solid #000;
border-radius: 6px;
box-shadow: 0px 4px 2px rgba(0,0,0,0.3);
background-color: rgb(203,243,65);
font: 11px sans-serif;
color: #555;
opacity: 0;
}
.Mbuttonwrap {
display: block;
position: absolute;
top: 245px;
margin: auto;
left: 0; right: 0;
height: 26px;
width: 100%;
}
.Mbuttons {
display: inline-block;
width: 69px;
height: 22px;
border: none;
margin: 0px 3.1px 0px 3.1px;
background-color: transparent;
font: bold 11px sans-serif;
color: #555;
border-radius: 20px;
cursor: pointer;
box-shadow: 0px 2px 2px rgba(0,0,0,0.5);
outline: 0;
}
.Mbuttons:hover {
background-color: rgb(213,253,75);
}
.Mbuttons:active {
position: relative;
top: 1px;
box-shadow: 0px 1px 2px rgba(0,0,0,0.8);
}
.Mchckboxwrap {
display: block;
position: absolute;
top: 274px;
left: 289px;
width: 87px;
height: 21px;
line-height: 22px;
border: 0.5px solid #000;
color: #555;
background: #fff;
-webkit-user-select: none;
-moz-user-select: none;
}
.Mselect {
display: block;
position: absolute;
top: 274px;
left: 6px;
width: 88px;
height: 22px;
border: 0.5px solid #000;
padding-left: 5%;
background: #fff;
font: bold 11px sans-serif;
color: #555;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
outline: 0;
}
::selection {
color: #fff;
background: #555;
}
.Mchckbox {
margin-top: 1px;
vertical-align: middle;
cursor: pointer;
outline: 0;
}
.modal {
display: none;
position: absolute;
margin: auto;
top: 0;right: 0;bottom: 0;left: 0;
background: rgba(0,0,0,0.5);
-webkit-user-select: none;
-moz-user-select: none;
opacity: 0;
text-align: center;
}
.modal > div {
display: inline-block;
position: relative;
width: 250px;
height: 70px;
margin: 10% auto;
padding: 10px;
border: 0.5px solid #000;
border-radius:6px;
background-color: rgb(203,243,65);
font: bold 11px sans-serif;
color: #555;
box-shadow: 4px 4px 2px rgba(0,0,0,0.3);
text-align: center;
}
.sliderWrap {
display: block;
position: absolute;
top: 274px;
margin:auto;padding: 0;
left: 0; right: 0;
width: 184px;
height: 21px;
border: 0.5px solid #000;
background: #fff;
font: bold 11px sans-serif;
color: #555;
line-height: 21px;
text-align: center;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
outline: 0;
}
input[type=range] {
-webkit-appearance: none;
width: 50%;
margin: 0;padding: 0;
vertical-align: middle;
}
input[type=range]:focus {
outline: none;
}
input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 4px;
cursor: pointer;
background: #666;
}
input[type=range]::-webkit-slider-thumb {
box-shadow: 1px 1px 0.5px rgba(0, 0, 0, 0.5);
border: none;
height: 10px;
width: 20px;
border-radius: 5px;
background: #ffffff;
cursor: pointer;
-webkit-appearance: none;
margin-top: -3px;
}
input[type=range]:focus::-webkit-slider-runnable-track {
background: #666;
}
input[type=range]::-moz-range-track {
width: 100%;
height: 4px;
cursor: pointer;
background: #666;
}
input[type=range]::-moz-range-thumb {
box-shadow: 1px 1px 0.5px rgba(0, 0, 0, 0.5);
height: 10px;
width: 20px;
border: none;
border-radius: 5px;
background: #ffffff;
cursor: pointer;
}
input[type=range]::-ms-thumb {
height: 10px;
width: 20px;
border: none;
border-radius: 5px;
background: #ffffff;
box-shadow: 1px 1px 0.5px rgba(0, 0, 0, 0.5);
cursor: pointer;
}
input[type=range]::-ms-track {
width: 100%;
height: 4px;
cursor: pointer;
background: transparent;
border: 5px solid transparent;
color: transparent;
}
input[type=range]::-ms-fill-lower {
background: #666;
}
input[type=range]::-ms-fill-upper {
background: #666;
}
::-ms-tooltip {
display: none;
}
select::-ms-expand {
display: none;
}
It would be best to get Firefox to fix the issue (if indeed it is a Firefox bug with automations). Having said that, you could probably make all the browsers be consistent by using an AudioBufferSource node that has a precomputed click waveform that you want. Just generate a sine wave, ramp it up and down as you want (manually) and play that back at regular intervals.
Not great, but it should be cross-platform.
AFAIK this issue is not specific to Firefox, although looking at your code, I'm unsure why it doesn't happen in other browsers.
The problem is that the moment you schedule a *rampToValueAtTime to an audible source when that source is not currently interpolating between two ramp points, the "clicking" sound occurrs, possibly due to how the underlying implementation will immediately start taking the new ramp point into consideration, even if it's scheduled to happen the future.
The clicking sound will also be heard if you schedule a new ramp point between two points between which interpolation is occurring.
What I came up with as a workaround solution is either using an alternative approach to gradually changing AudioParam values, setTargetAtTime, or setting the value property of the AudioParam to the first ramp point value. Not setValueAtTime, but assigning to the value property itself, before anything audible happens on the given branch.
setTargetAtTime
You'll be needing neither cancelScheduledValues nor setValueAtTime, just two calls to setTargetAtTime, which is just a setValueAtTime with an exponential interpolation with a specified length.
var metronome = {
start: function repeat() {
now = audio.currentTime;
//Play the osc with a super fast attack and release so it sounds like a click
tickVol.gain.setTargetAtTime(1, now, 0.01);
tickVol.gain.setTargetAtTime(0, now + 0.01, 0.01);
//Repeat this function every half second
click = setTimeout(repeat, 500);
}
}
Live demo on JSFiddle
Related
javascript on a webpage displaying text wrongly
I have JS code on a webpage that loads questions in from mysql db and displays the text . What happens is that it cuts off words at the end of the line and continues the word on the next line at the start. So all text across the screen starts/ends at the same point. This seems to be the code where it displays the text. For example the text will look like at the end of a line 'cont' and then on next line at the start 'inue'. How do i fix this? var questions = <?=$questions;?>; // Initialize variables //------------------------------------------------------------------ var tags; var tagsClass = ''; var liTagsid = []; var correctAns = 0; var isscorrect = 0; var quizPage = 1; var currentIndex = 0; var currentQuestion = questions[currentIndex]; var prevousQuestion; var previousIndex = 0; var ulTag = document.getElementsByClassName('ulclass')[0]; var button = document.getElementById('submit'); var questionTitle = document.getElementById('question'); //save class name so it can be reused easily //if I want to change it, I have to change it one place var classHighlight = 'selected'; // Display Answers and hightlight selected item //------------------------------------------------------------------ function showQuestions (){ document.body.scrollTop = 0; // For Safari document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera if (currentIndex != 0) { // create again submit button only for next pages ulTag.innerHTML =''; button.innerHTML = 'Submit'; button.className = 'submit'; button.id = 'submit'; if(quizPage<=questions.length){ //update the number of questions displayed document.getElementById('quizNumber').innerHTML = quizPage; } } //Display Results in the final page if (currentIndex == (questions.length)) { ulTag.innerHTML = ''; document.getElementById('question').innerHTML = ''; if(button.id == 'submit'){ button.className = 'buttonload'; button.innerHTML = '<i class="fa fa-spinner fa-spin"></i>Loading'; } showResults(); return } questionTitle.innerHTML = "Question No:" + quizPage + " "+currentQuestion.question.category_name +"<br/>"+ currentQuestion.question.text; if(currentQuestion.question.filename !== ''){ var br = document.createElement('br'); questionTitle .appendChild(br); var img = document.createElement('img'); img.src = currentQuestion.question.filename; img.className = 'imagecenter'; img.width = 750; img.height = 350; questionTitle .appendChild(img); } // create a for loop to generate the options and display them in the page for (var i = 0; i < currentQuestion.options.length; i++) { // creating options var newAns = document.createElement('li'); newAns.id = 'ans'+ (i+1); newAns.className = "notSelected listyle"; var textAns = document.createTextNode(currentQuestion.options[i].optiontext); newAns.appendChild(textAns); if(currentQuestion.options[i].file !== ''){ var br = document.createElement('br'); newAns .appendChild(br); var img1 = document.createElement('img'); img1.src = currentQuestion.options[i].file; img1.className = 'optionimg'; img1.width = 250; img1.height = 250; newAns .appendChild(img1); newAns .appendChild(br); } var addNewAnsHere = document.getElementById('options'); addNewAnsHere.appendChild(newAns); } //.click() will return the result of $('.notSelected') var $liTags = $('.notSelected').click(function(list) { list.preventDefault(); //run removeClass on every element //if the elements are not static, you might want to rerun $('.notSelected') //instead of the saved $litTags $liTags.removeClass(classHighlight); //add the class to the currently clicked element (this) $(this).addClass(classHighlight); //get id name of clicked answer for (var i = 0; i < currentQuestion.options.length ; i++) { // console.log(liTagsid[i]); if($liTags[i].className == "notSelected listyle selected"){ //store information to check answer tags = $liTags[i].id; // tagsClass = $LiTags.className; tagsClassName = $liTags[i]; } } }); //check answer once it has been submitted button.onclick = function (){ if(button.id == 'submit'){ button.className = 'buttonload'; button.innerHTML = '<i class="fa fa-spinner fa-spin"></i>Loading'; } setTimeout(function() { checkAnswer(); }, 100); }; } //self calling function showQuestions(); The website is on my local now but i can upload a screenimage if need be and the whole code of the webpage. Or is the issue in html? edit: here is html/css code <style> /*======================================================== Quiz Section ========================================================*/ /*styling quiz area*/ .main { background-color: white; margin: 0 auto; margin-top: 30px; padding: 30px; box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24); /*white-space: nowrap;*/ } /*Editing the number of questions*/ .spanclass { font-size: x-large; } #pages{ border: 3px solid; display: inline-flex; border-radius: 0.5em; float: right; } #question{ word-break: break-all; } /*format text*/ p { text-align: left; font-size: x-large; padding: 10px 10px 0; } .optionimg{ border: 2px solid black; border-radius: 1.5em; } /*Form area width*/ /*formatting answers*/ .listyle { list-style-type: none; text-align: left; background-color: transparent; margin: 10px 5px; padding: 5px 10px; border: 1px solid lightgray; border-radius: 0.5em; font-weight: normal; font-size: x-large; display: inline-grid; width: 48%; height: 300px; overflow: auto; } .listyle:hover { background: #ECEEF0; cursor: pointer; } /*Change effect of question when the questions is selected*/ .selected, .selected:hover { background: #FFDEAD; } /*change correct answer background*/ .correct, .correct:hover { background: #9ACD32; color: white; } /*change wrong answer background*/ .wrong, .wrong:hover { background: #db3c3c; color: white; } /*======================================================== Submit Button ========================================================*/ .main button { text-transform: uppercase; width: 20%; border: none; padding: 15px; color: #FFFFFF; } .submit:hover, .submit:active, .submit:focus { background: #43A047; } .submit { background: #4CAF50; min-width: 120px; } /*next question button*/ .next { background: #fa994a; min-width: 120px; } .next:hover, .next:active, .next:focus { background: #e38a42; } .restart { background-color: } /*======================================================== Results ========================================================*/ .circle{ position: relative; margin: 0 auto; width: 200px; height: 200px; background: #bdc3c7; -webkit-border-radius: 100px; -moz-border-radius: 100px; border-radius: 100px; overflow: hidden; } .fill{ position: absolute; bottom: 0; width: 100%; height: 80%; background: #31a2ac; } .score { position: absolute; width: 100%; top: 1.7em; text-align: center; font-family: Arial, sans-serif; color: #fff; font-size: 40pt; line-height: 0; font-weight: normal; } .circle p { margin: 400px; } /*======================================================== Confeeti Effect ========================================================*/ canvas{ position:absolute; left:0; top:11em; z-index:0; border:0px solid #000; } .imagecenter{ display: block; margin: 0 auto; } .buttonload { background-color: #04AA6D; /* Green background */ border: none; /* Remove borders */ color: white; /* White text */ padding: 12px 24px; /* Some padding */ font-size: 16px; /* Set a font-size */ } /* Add a right margin to each icon */ .fa { margin-left: -12px; margin-right: 8px; } #media only screen and (max-width: 900px){ .listyle { width: 100% !important; height: auto !important; } .imagecenter { width: 100% !important; } .listyle img{ width: inherit !important; height: unset !important; } .ulclass { padding:0px !important; } } </style> <!-- Main page --> <div class="main"> <!-- Number of Question --> <div class="wrapper" id="pages"> <span class="spanclass" id="quizNumber">1</span><span class="spanclass">/<?=$count?></span> </div> <!-- Quiz Question --> <div class="quiz-questions" id="display-area"> <p id="question"></p> <ul class="ulclass" id="options"> </ul> <div id="quiz-results" class="text-center"> <button type="button" name="button" class="submit" id="submit">Submit</button> </div> </div> </div> <canvas id="canvas"></canvas> <script src="https://code.jquery.com/jquery-3.2.1.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> <script>
I'm guessing that #question{ word-break: break-all; } is probably the culprit then? – CB..yes that fixed it:)
"clone" audio player to control another?
I have a list of players that user scrolls through and I'm trying create an always-visible player control, so user don't have to scroll through the list looking for the player that is playing sound. I'd like somehow "clone" <audio> element to make it not actually play anything, just show information about currently playing media in another player and visually be identical to original player. Currently I generate my own "player" and use media events to gather/display information about playing player, but it probably looks different in different browsers/systems. Any suggestions? const content = document.getElementById("content"); const eventHandler = (e => { const remote = document.getElementById("remote"), //our custom "player" progress = remote.querySelector(".progress"), time = remote.querySelector(".time"), position = time.querySelector(".position"), duration = time.querySelector(".duration"); ; let player = null; //this will hold current player //list of event handlers const handler = { play: e => { if (player && player !== e.target) player.pause(); player = e.target; remote.classList.add("player"); remote.classList.add("play"); content.querySelector(".selected")?.classList.remove("selected"); player.classList.add("selected"); handler.timeupdate(e); handler.durationchange(e); }, pause: e => { if (player.paused) remote.classList.remove("play"); }, timeupdate: e => { if (player !== e.target) return; position.textContent = readableTime(e.target.currentTime); progress.value = e.target.currentTime; }, durationchange: e => { if (player !== e.target) return; duration.textContent = readableTime(e.target.duration); progress.max = e.target.duration; } } const readableTime = (t, ms) => { const seconds = ~~t, minutes = ~~(seconds / 60); return ("" + minutes).padStart(1, "0") + ":" + ("" + seconds % 60).padStart(2, "0") + (ms ? "." + (t.toFixed(1)+".0").substr((""+t).indexOf(".")+1, 1) : ""); } remote.querySelector(".playpause").addEventListener("click", e => { player[player.paused ? "play" : "pause"](); }); progress.addEventListener("input", e => { player.currentTime = progress.value; }); return e => { try{handler[e.type](e)}catch(er){console.error(er)}; }; })(); document.addEventListener("play", eventHandler, true); document.addEventListener("pause", eventHandler, true); document.addEventListener("timeupdate", eventHandler, true); /* show players list */ { const files = ["Zh[Wci", "ceei[", "iY_\\_", "ZWdY[", "^eki[", "fefZWdY["]; let file, player = document.createElement("audio"); player.setAttribute("controls", ""); player.setAttribute("nodownload", ""); while((file = files.splice(~~(Math.random() * files.length), 1)).length) { /* https://www.bensound.com/ */ player.src = `^jjfi0%%mmm$X[diekdZ$Yec%X[diekdZ#cki_Y%X[diekdZ#${file[0]}$cf)`.replace(/./g,a=>String.fromCharCode(a.charCodeAt(0)+10) ) ; content.appendChild(player); player = player.cloneNode(true); } } .content { display: flex; margin: auto; } #content { display: grid; max-height: 7em; overflow-y: auto; margin-top: 1em; } #remote { font-family: Roboto-Regular, Roboto, sans-serif; font-size: 14px; display: flex; user-select: none; cursor: default; border: 1px solid black; background-color: white; color: black; border-radius: 2em; width: fit-content; height: 2em; padding: 0.5em 1.3em; align-items: center; gap: 10px; max-height: 2em; } #remote:not(.player) { pointer-events: none; opacity: 0.5; } /* play/pause button */ #remote .playpause { height: 1em; width: 1em; padding: 0.5em; margin: auto -0.3em; border-radius: 100%; text-align: center; cursor: pointer; } /* play */ #remote .playpause:after { --size: 1em; width: var(--size); height: var(--size); content: ""; display: inline-block; box-sizing: border-box; border-style: solid; border-width: calc(var(--size) / 2) 0 calc(var(--size) / 2) var(--size); border-color: transparent transparent transparent black; transition: all 100ms ease-in-out; } /* pause */ #remote.play .playpause:after { border-style: double; border-width: 0 0 0 var(--size); } #remote .playpause:hover { background-color: rgba(32, 33, 36, 0.06); } #remote .progress { cursor: pointer; } #remote span { } audio { border: 2px solid transparent; border-radius: 2em; height: 2.5em; } audio.selected { outline: 0; border-color: orange; } <div id="remote"> <span class="playpause"></span> <span class="time"> <span class="position">0:00</span> / <span class="duration">0:00</span> </span> <input type="range" class="progress" min="0" value="0"> </div> <div class="content"> <div id="content"> </div> </div>
Keep Lines Containing
I am creating a "Keep Lines containing" project. I almost completed this task. But "Search Lines for" is working only 1 line. Multiple "Search Lines for" is not working. I need "Search Lines for" in multiple lines. All used HTML, CSS & javascript codes are here I created a Codepen page for it. Please check : https://codepen.io/coderco/pen/LYGQyqr function loadfile(fileid, loadid) { document.getElementById(loadid).value = 'Loading...'; setTimeout(function() { loadfile2(fileid, loadid) }, 1000); } function loadfile2(fileid, loadid) { if (!window.FileReader) { document.getElementById(loadid).value = 'Your browser does not support HTML5 "FileReader" function required to open a file.'; } else { fileis = document.getElementById(fileid).files[0]; var fileredr = new FileReader(); fileredr.onload = function(fle) { var filecont = fle.target.result; document.getElementById(loadid).value = filecont; } fileredr.readAsText(fileis); } } function savefile(saveasid, saveid) { if (!window.Blob) { alert('Your browser does not support HTML5 "Blob" function required to save a file.'); } else { var txtwrt = document.getElementById(saveid).value; if (document.getElementById('dos').checked == true) txtwrt = txtwrt.replace(/\n/g, '\r\n'); var textblob = new Blob([txtwrt], { type: 'text/plain' }); var saveas = document.getElementById(saveasid).value; var dwnlnk = document.createElement('a'); dwnlnk.download = saveas; dwnlnk.innerHTML = "Download File"; if (window.webkitURL != null) { dwnlnk.href = window.webkitURL.createObjectURL(textblob); } else { dwnlnk.href = window.URL.createObjectURL(textblob); dwnlnk.onclick = destce; dwnlnk.style.display = 'none'; document.body.appendChild(dwnlnk); } dwnlnk.click(); } } function destce(event) { document.body.removeChild(event.target); } function cleartext() { document.getElementById('input_output').value = ''; document.getElementById('removed').innerHTML = ''; document.getElementById('removed_box').value = ''; } function SelectAll(id) { document.getElementById(id).focus(); document.getElementById(id).select(); } var fieldnum = 0; var fieldtype = ''; var cacherem = 'no'; var enableregex = 'no'; function makeregexp() { var regexpoutarr = new Array(); for (var x = 0; x < (fieldnum + 1); x++) { regexpoutarr[x] = document.getElementById('addfield' + x).value.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'); } var regexpout = ''; if (fieldtype == 'AND') regexpout = '((?=.*' + regexpoutarr.join(')(?=.*') + ').*)'; if (fieldtype == 'OR') regexpout = '(' + regexpoutarr.join('|') + ')'; if (fieldtype == '') regexpout = document.getElementById('addfield0').value.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'); return regexpout; } function removelines(whatlines) { var textin = document.getElementById('input_output').value.replace(/\r/g, ''); var toremove = makeregexp(); var textinarr = textin.split('\n'); var textinarrcnt = textinarr.length; var textoutarr = new Array(); var textoutarrcnt = 0; var linesremovedcnt = 0; var casen = 'i'; if (document.getElementById('case_sen').checked == true) casen = ''; if (enableregex == 'yes') toremove = document.getElementById('addfield0').value; else toremove = makeregexp(); var killfun = 'no'; try { var toremoveregx = new RegExp(toremove, casen); } catch (err) { alert('Something is incorrect (' + err + ') within your regular expression.\nBe sure special characters .*+?^=!:${}()|\\ used as literals have been escaped with a backslash.'); killfun = 'yes'; } if (killfun == 'no') { if (whatlines == 'containing' && cacherem == 'no') { for (var x = 0; x < textinarrcnt; x++) { if (toremoveregx.test(textinarr[x]) == false) { textoutarr[textoutarrcnt] = textinarr[x]; textoutarrcnt++; } else linesremovedcnt++; } } if (whatlines == 'notcontaining' && cacherem == 'no') { for (var x = 0; x < textinarrcnt; x++) { if (toremoveregx.test(textinarr[x]) == true) { textoutarr[textoutarrcnt] = textinarr[x]; textoutarrcnt++; } else linesremovedcnt++; } } var removedcachearr = new Array(); if (whatlines == 'containing' && cacherem == 'yes') { for (var x = 0; x < textinarrcnt; x++) { if (toremoveregx.test(textinarr[x]) == false) { textoutarr[textoutarrcnt] = textinarr[x]; textoutarrcnt++; } else { removedcachearr[linesremovedcnt] = textinarr[x]; linesremovedcnt++; } } } if (whatlines == 'notcontaining' && cacherem == 'yes') { for (var x = 0; x < textinarrcnt; x++) { if (toremoveregx.test(textinarr[x]) == true) { textoutarr[textoutarrcnt] = textinarr[x]; textoutarrcnt++; } else { removedcachearr[linesremovedcnt] = textinarr[x]; linesremovedcnt++; } } } var textout = textoutarr.join('\n'); document.getElementById('input_output').value = textout; if (cacherem == 'yes') { var removedcache = removedcachearr.join('\n'); document.getElementById('removed_box').value = removedcache; } document.getElementById('removed').innerHTML = '' + linesremovedcnt + ' removed / ' + textoutarrcnt + ' remain.'; } } function addfield(field) { if (field == 'reset') { document.getElementById('inputfields').innerHTML = '<input type="text" id="addfield0" value="" style="width:100%;" />' document.getElementById('andbttn').style.display = 'inline-block'; document.getElementById('orbttn').style.display = 'inline-block'; fieldnum = 0; fieldtype = ''; } else { fieldnum++; if (fieldnum == 1) { if (field == 'andfield') { document.getElementById('orbttn').style.display = 'none'; fieldtype = 'AND'; } else { fieldtype = 'OR'; document.getElementById('andbttn').style.display = 'none'; } } var newfield = fieldtype + '<input type="text" id="addfield' + fieldnum + '" value="" style="width:100%;" />'; var newdiv = document.createElement('div'); newdiv.innerHTML = newfield; document.getElementById('inputfields').appendChild(newdiv); } resizepage(); } function disrem() { var chkedstate = document.getElementById('dremoved').checked; if (chkedstate == true) { document.getElementById('removed_box').style.display = 'inline-block'; cacherem = 'yes'; } else { cacherem = 'no'; document.getElementById('removed_box').value = ''; document.getElementById('removed_box').style.display = 'none'; } resizepage(); } function selectele(eleid) { if (document.selection) { var range = document.body.createTextRange(); range.moveToElementText(document.getElementById(eleid)); range.select(); } else { var range = document.createRange(); range.selectNode(document.getElementById(eleid)); window.getSelection().addRange(range); } } function regexsrch() { var chkedstate = document.getElementById('regex_srch').checked; if (chkedstate == true) { addfield('reset'); enableregex = 'yes'; document.getElementById('addfielddiv').innerHTML = '<div style="padding:3px 0px 3px 0px;"><input type="checkbox" id="regex_srch" onclick="regexsrch();" CHECKED />Enable regular expression search. ' + 'Use <span id="catordog" style="color:#990000;" onclick="selectele(this.id)">(cat|dog|bird)</span> for cat OR dog OR bird. Use <span id="catanddog" style="color:#990000;" onclick="selectele(this.id)">((?=.*cat)(?=.*dog)(?=.*bird).*)</span> for cat AND dog AND bird. ' + 'Remember to escape special characters .*+?^=!:${}()|\\ with a backslash when used as literals within a regular expression. Use the <a target="_blank" href="" style="color:#0000FF;">Escape Literal Characters</a> tool. ' + 'Learn more about regular expressions visit <a rel="nofollow" target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions" style="color:#0000FF;">developer.mozilla.org</a>.</div>'; } else { document.getElementById('addfield0').value = ''; enableregex = 'no'; document.getElementById('addfielddiv').innerHTML = 'Add <input type="button" id="andbttn" value="AND" onClick="addfield(\'andfield\');" /> ' + '<input type="button" id="orbttn" value="OR" onClick="addfield(\'orfield\');" /> search field. ' + '<input type="button" value="Reset" onClick="addfield(\'reset\');" /> ' + '<input type="checkbox" id="regex_srch" onclick="regexsrch();" />Enable regular expression search.'; } resizepage(); } html { height: 100%; } body { height: 100%; margin: 0px; padding: 0px; font-family: arial; font-size: 16px; line-height: 1.7; } h1 { display: block; font-size: 18px; font-weight: bold; margin: 0px; padding: 0px; } .se-for { font-size: 27px; font-weight: bold; color: #b93207; padding-top: 54px; } .contentt, .wordd { display: block; margin: 0px; padding: 8px 10px 8px 10px; overflow: scroll; font-family: arial; font-size: 16px; line-height: 1.7; color: #000000; background-color: #FFFFFF; border: 1px solid #000000; outline: none; resize: none; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; border-radius: 4px 4px 4px 4px; width: 100%; font-size: 18px; } .contentt { height: 400px; overflow: auto; } .wordd { height: 99px; overflow: auto; } input { display: inline-block; height: 33px; line-height: 1; vertical-align: middle; font-size: 16px; outline: none; resize: none; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } input::-moz-focus-inner { border: 0; padding: 0 } input[type='radio'] { width: 20px; height: 20px; vertical-align: middle; margin: 0px; padding: 0px; font-size: 16px; } input[type='checkbox'] { width: 20px; height: 20px; vertical-align: middle; margin: 0px; padding: 0px; font-size: 16px; } input[type='text'] { width: auto; margin: 3px 0px 3px 0px; padding: 0px 10px 0px 10px; font-family: arial; color: #000000; background-color: #FFFFFF; border: 1px solid #000000; border-radius: 12px; } input[type='button'] { width: auto; margin: 3px 0px 3px 0px; padding: 0px 10px 0px 10px; font-family: arial; font-weight: bold; color: #000000; border: 1px solid #000000; background-color: #FFFFFF; cursor: pointer; border-radius: 5px; background: #b93207; color: #fff; border: #b93207; } input[type='button']:hover { color: #f9f900; } input[type='button']:hover {} input[type='file'] { width: 92px; border-radius: 12px; overflow: hidden; padding: 0px; margin: 0px 0px 0px -92px; -moz-opacity: 0; opacity: 0; cursor: pointer; } input[type='file']::-webkit-file-upload-button { cursor: pointer; } div { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } table { border-collapse: collapse; } table, td { padding: 0px; } .buttonz { width: 100%; display: inline-block; } #menu { position: absolute; z-index: -1; left: 0px; top: 0px; width: 0px; height: 0%; margin: 0px; overflow: auto; background-color: #E1E1D2; border-right: 1px solid #000000; } div.navcat { padding: 10px 0px 5px 12px; font-size: 18px; font-weight: bold; font-style: italic; } div.navdiv { height: 2px; padding: 0px; margin: 18px 10px 13px 10px; background-color: #000000; } a.nav { display: inline-block; padding: 0px; margin: 10px 0px 10px 10px; text-decoration: underline; color: #000000; } #toolpadding { padding: 10px 10px 10px 10px; } #tool { width: 1100px; margin: 0px; padding: 0px; background-color: #fff; margin: 0 auto; } <div id="tool"> <div id="toolpadding"> <div id="topdiv"> <div class="se-for">Search lines for:</div> <div id="inputfields" style="padding-top:4px;"> <textarea type="text" id="addfield0" class="wordd">Three</textarea> </div> <div class="buttonz"> <input type="button" value="Keep Lines Containing" onClick="if(document.getElementById('addfield0').value!='') {removelines('notcontaining');}" /> <input type="checkbox" id="case_sen" />Case sensitive. </div> </div> <div id="middiv" style="height:120px;"> <textarea id="input_output" class="contentt" wrap="off"> One One One One One One One One One One One One One One One Three Three Three Three Three Three Three Three Three Three Two Two Two Two Two Two Two Two Two Two Two Two Two Two Two Two Two Two Two Two Two Two Two Two Two Two Two Two Two Two Three Three Three Three Three Three Three Three Three Three Three Three Three Three Three Three Three Three Three Three Four Four Four Four Four Four Four Four Four Four Four Four Five Five Five Five Five Five Five Five Five Five Five Five Three Three Three Three Three Three Three Three Three Three Five Five Five Five Five Five Five Five Five Five Five Five Three Three Three Three Three Three Three Three Three Three </textarea> </div> <div id="btmdiv"> <textarea id="removed_box" rows="4" style="display:none; width:100%; margin-top:10px;" wrap="off"> Removed Line Box - Removed/extracted lines will display here.</textarea> </div> </div> </div>
localStorage does not save/show items
I do my ToDo list. (I learn vanilla JS). And I have some problem with saving some items to localStorage. When I use Google chrome(F12), I see undefiend. Maybe, I do not save correctly to localStorage. I tried to change var task to array, but it does not help. Pleas, show me my mistakes. I know, my code must be rewritten, it is my first code on JS. P.s. in console (in stackOverflow) I have that error { "message": "Uncaught SyntaxError: Unexpected identifier", "filename": "https://stacksnippets.net/js", "lineno": 348, "colno": 6 } but in my browser not. var task = document.querySelector("ul"); var forTask; function toLocal(){ forTask = task.innerHTML; localStorage.setItem("forLocal",forTask); } function newElement(newChild) { let btnDel= document.createElement("button"); btnDel.className = "fa fa-trash-o"; let myEd = document.getElementById("myEdit"); let spanClose1 = document.getElementsByClassName("close1")[0]; let spanRedact = document.getElementsByClassName("redact")[0]; let myDel = document.getElementById("myDelete"); let spanClose = document.getElementsByClassName("close")[0]; let spanYes = document.getElementsByClassName("yes")[0]; //create button let divWithBut = document.createElement("div"); divWithBut.className = "forButt"; let btnRedact = document.createElement("button"); btnRedact.className = "fa fa-pencil"; //redact but btnRedact.onclick = function(){ myEd.style.display = "block"; let editText = document.getElementById("editText"); let divWithText = divWithBut.parentElement.getElementsByClassName("todoPost")[0]; editText.value = divWithText.innerHTML; editText.currentTarget; spanRedact.onclick = function(){ divWithText.textContent = editText.value; divWithText.className = "todoPost"; myEd.style.display = "none"; }; spanClose1.onclick = function() { myEd.style.display = "none"; }; } /*************************** */ /*done but*/ let doneBut = document.createElement("button"); doneBut.className = "fa fa-check-circle-o"; doneBut.onclick = function(){ let divWithText = divWithBut.parentElement.getElementsByClassName("todoPost")[0]; divWithText.classList.toggle("checked"); } /******************* */ divWithBut.appendChild(btnRedact); divWithBut.appendChild(doneBut); divWithBut.appendChild(btnDel); /******************/ //for index let indexDiv = document.createElement("div"); indexDiv.className = "indexDiv"; let numbInd = 1; indexDiv.innerHTML = numbInd; /*********************************** */ //create arrow let divWithArrow = document.createElement("div"); divWithArrow.className = "myArrow"; let arrowUP = document.createElement("i"); arrowUP.className = "fa fa-chevron-up"; let arrowDown = document.createElement("i"); arrowDown.className = "fa fa-chevron-down"; divWithArrow.appendChild(arrowUP); divWithArrow.appendChild(arrowDown); //for date let date = new Date(); let curr_date = date.getDate(); let curr_month = date.getMonth()+1; let curr_year = date.getFullYear(); let curr_hour = date.getHours(); let curr_minutes = date.getMinutes(); let d = (curr_date + "." + curr_month + "." + curr_year+"<br>"+curr_hour+":"+curr_minutes); let divTime = document.createElement("div"); divTime.style.textAlign = "center";; divTime.innerHTML = d; //***************************/ let div1 = document.createElement("div"); div1.className = "timeComent"; let myli = document.createElement("li"); myli.className = "todoPost"; let addField = document.getElementById("addField").value; task = document.createTextNode(addField); myli.appendChild(task); div1.appendChild(divTime); div1.appendChild(indexDiv); div1.appendChild(divWithArrow); div1.appendChild(myli); divWithBut.style.display = "flex"; div1.appendChild(divWithBut); if (addField === '') { alert("You must write something!"); } else { document.getElementById("forToDo").appendChild(div1); toLocal(); } document.getElementById("addField").value = ""; //delete but btnDel.onclick = function(){ myDel.style.display = "block"; spanClose.onclick = function() { myDel.style.display = "none"; }; spanYes.onclick = function() { myDel.style.display = "none"; div1.remove(); }; } toLocal(); } if(localStorage.getItem("forLocal")){ task.innerHTML = localStorage.getItem("forLocal"); } *{ margin: 0; padding: 0; } header{ width: 100%; display: flex; align-items: center; align-content: center; justify-content: center; overflow: auto; } .firstBar{ width: 100%; display: flex; align-items: center; align-content: center; justify-content: center; overflow: auto; } .indexDiv{ font-style: normal; text-align: center; color: #fff; width: 15px; height: 20px; margin: 10px; background-color: #888; } .fafaArrow{ font-size: 24px; color: #000; } .timeComent{ margin-top: 15px; margin-bottom: 15px; display: flex; justify-content:center; align-items: center; } .numberpost{ padding: 5px; color: rgb(255, 255, 255); background: rgb(141, 112, 112); } .todoPost{ background-color: #eee; width: 50%; margin: 5px; overflow: auto; text-align: justify; } .shadow { background: rgba(102, 102, 102, 0.5); width: 100%; height: 100%; position: absolute; top: 0; left: 0; display: none; } .window { width: 300px; height: 50px; text-align: center; padding: 15px; border: 3px solid #0000cc; border-radius: 10px; color: #0000cc; position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto; background: #fff; } .shadow:target {display: block;} .redact { display: inline-block; border: 1px solid #0000cc; color: #0000cc; margin: 10px; text-decoration: none; background: #f2f2f2; font-size: 14pt; cursor:pointer; right: 0; top: 0; padding: 12px 16px 12px 16px; } .redact:hover { background-color: #68f462; color: white;} .close{ display: inline-block; border: 1px solid #0000cc; color: #0000cc; margin: 10px; text-decoration: none; background: #f2f2f2; font-size: 14pt; cursor:pointer; right: 0; top: 0; padding: 12px 16px 12px 16px; } .close:hover{ background-color: #f44336; color: white; } /* Style the close button */ .close3 { position: absolute; right: 0; top: 0; padding: 12px 16px 12px 16px; } .yes { display: inline-block; border: 1px solid #0000cc; color: #0000cc; margin: 10px; text-decoration: none; background: #f2f2f2; font-size: 14pt; cursor:pointer; right: 0; top: 0; padding: 12px 16px 12px 16px; } .yes:hover{ background-color: #68f462; color: white; } .close1{ display: inline-block; border: 1px solid #0000cc; color: #0000cc; margin: 10px; text-decoration: none; background: #f2f2f2; font-size: 14pt; cursor:pointer; right: 0; top: 0; padding: 12px 16px 12px 16px; } .close1:hover{ background-color: #f44336; color: white; } /* When clicked on, add a background color and strike out text */ div li.checked { background: #888; color: #fff; text-decoration: line-through; } /* Add a "checked" mark when clicked on */ div li.checked::before { content: ''; position: absolute; border-color: #fff; border-style: solid; border-width: 0 2px 2px 0; top: 10px; left: 16px; transform: rotate(45deg); height: 15px; width: 7px; } <!DOCTYPE html> <html> <head> <title>TO DO List</title> <link rel="stylesheet" type="text/css" href="styles/style.css" > <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> </head> <body> <header> <input id="addField" type="text" size="70%" placeholder="Task" name="Task"> <button type="button" onclick="newElement()">Add</button> </header> <div> <div class="firstBar"> <div class="fafaArrow"> <i class="fa fa-caret-up" ></i> <i class="fa fa-caret-down"></i> <input class="inptxt" type="text" size="50%" name="Task"> <i class="fa fa-filter"></i> </div> </div> </div> <ul id="forToDo" > </ul> <div id="myDelete" class="shadow"> <div class="window">Delete item?<br> <span class="yes">Yes</span> <span class="close">No</span> </div> </div> <div id="myEdit" class="shadow"> <div class="window"> Edit text?<br> <label> <textarea id="editText"></textarea> </label> <span class="redact">Save</span> <span class="close1">Cancel</span> </div> </div> <script src="js/script2.js"></script> </body> </html>
When you add an element to the page, at a certain point you do this task = document.createTextNode(addField); Since task is a global variable (you declared it at the top), you're overshadowing it with the TextNode you're creating, so that when you then call toLocal and you do forTask = task.innerHTML; task has no innerHTML attribute, so it returns undefined. Also, for some reason, you call toLocal again at the end of newElement. It's not the problem but it's something you may want to think about. I'm not sure it's what you want. #TakayashiHarano gave a couple of hints to solve this, but I'm not sure what you want is just to have the latest element in the local storage. So I would re-write toLocal so that it takes a string (the text of the item) as input, writes it at the end of a JSON array (already populated with what was in the local storage previously), and puts the array back in local storage. function toLocal(toAdd) { let storage = localStorage.getItem('forLocal'); if (storage === null) { storage = []; } else { storage = JSON.parse(storage); } storage.push(toAdd); localStorage.setItem('forLocal', JSON.stringify(storage)); } Then you should modify the part of the code that reads the local storage (the one at the end) to basically simulate adding a new item as you would do when creating a new task, but for each item in the parsed JSON coming from local storage. To be fair, your code needs a good dose of rewriting to achieve this, so I'll just leave you with this as an exercise.
The following changes are needed. 1 - Set up two variables separably for the following task variable. var task = document.querySelector("ul"); task = document.createTextNode(addField); For example, "ulElement" for the first one, and "task" for the second one. This is to prevent to override the previously defined value. 2 - Move the timing for obtaining the ul element and load localStorage. function onReady() { ulElement = document.querySelector("ul"); if(localStorage.getItem("forLocal")){ ulElement.innerHTML = localStorage.getItem("forLocal"); } } window.addEventListener('DOMContentLoaded', onReady, true); To ensure the element existence, document.querySelector() should be called after the DOMContentLoaded event fired. https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event 3 - Delete toLocal(); in the end of the newElement() function. As far as my testing code, there is no need this statement.
Custom Media player design to scale when width changed
I am having a Trouble with this custom media player, Media player not functioning properly. I have 2 problems: When full-screen it goes back to the default HTML5 player When i adjust the width of the video tag it throws all of the spacing out and ruins the player. HTML <!DOCTYPE HTML> <html> <head> <title>Video/Audio</title> <link rel='stylesheet' type='text/css' href='style.css' /> <style type="text/css"> </style> <script src='jquery.js'></script> <script src='javascript.js'></script> <script type='text/javascript'> $(document).ready(function() { $('video').videoPlayer({ 'playerWidth' : 1, 'videoClass' : 'video' }); }); </script> </head> <body> <div class="container" class="player"> <video width="700" height="400"> <source src="https://s3-eu-west-1.amazonaws.com/icevideos/151014+Cathodic+Protection+of+Highways/151014.PETERBOROUGH.CATHODICPROTECTION.HIGH1.mp4" type="video/mp4"> <source src="movie.webm" type="video/webm"> </video> </div> </body> </html> CSS body { font-size: 62.5%; padding: 0; margin: 0; } .player { background: grey; box-sizing: border-box; height: 40px; -moz-box-sizing: border-box; float: left; font-family: Arial, sans-serif; position: absolute; padding: 0; bottom: 4px; z-index: 2; opacity: 1; box-shadow: 0 0 10px rgba(0,0,0,0.3); -webkit-transition: opacity 0.3s ease-in; transition: opacity 0.3s ease-in; -moz-user-select: none; -webkit-user-select: none; user-select: none; width: 100%; } .video { position: relative; margin: 0px auto; } .video:hover .player { opacity: 1; } .player .progress { width: 60%; height: 20px; border-radius: 5px; background: #000; box-shadow: inset 0 -5px 10px rgba(0,0,0,0.1); float: left; cursor: pointer; margin: 12px 0 0 0; padding: 0; position: relative; font-variant: normal; margin-left: 20px; } .player .progress-bar { background: #FF6600; border-radius: 5px; height: 100%; position: relative; z-index: 999; width: 0; } .player .button-holder { position: relative; left: 10px; } .player .progress-button { background: #00bdff; box-shadow: 0 0 20px rgba(0,0,0,0.3); border-radius: 30px; width: 20px; height: 20px; position: absolute; left: -20px; text-decoration: overline; } .player [class^="buffered"] { background: rgba(255,255,255,0.1); position: absolute; top: 0; left: 30px; height: 100%; border-radius: 5px; z-index: 1; } .player .play-pause { display: inline-block; font-size: 3em; float: left; text-shadow: 0 0 0 #fff; color: #00bdff; width: 4%; padding: 4px 0 0 0; margin-left: 15px; cursor: pointer; font-variant: small-caps; } .player .play, .player .pause-button { -webkit-transition: all 0.2s ease-out; } .player .play .pause-button, .player .pause .play-button { display: none; } .player .pause-button { padding: 5px 2px; box-sizing: border-box; -moz-box-sizing: border-box; height: 34px; } .player .pause-button span { background: #FF6600; width: 8px; height: 24px; float: left; display: block; } .player .pause-button span:first-of-type { margin: 0 4px 0 0; } .player .time { color: #fff; font-weight: bold; font-size: 1.2em; position: absolute; width: 150px; margin-left: 425px; bottom: 3px; } .player .stime, .ttime { color: #fff; } .player .play:hover { text-shadow: 0 0 5px #fff; } .player .play:active, .pause-button:active span { text-shadow: 0 0 7px #fff; } .player .pause-button:hover span { box-shadow: 0 0 5px #fff; } .player .pause-button:active span { box-shadow: 0 0 7px #fff; } .player .volume { position: relative; float: left; width: 7%; height: 100%; margin-left: 70px; } .player .volume-icon { padding: 1.5%; height: 100%; cursor: pointer; box-sizing: border-box; -moz-box-sizing: border-box; -webkit-transition: all 0.15s linear; } .player .volume-icon-hover { background-color: grey; } .player .volume-holder { height: 100px; width: 80%; position: absolute; display: none; background: grey; left: 0; border-radius: 5px 5px 0 0; top: -100px; } .player .volume-bar-holder { background: black; width: 20px; box-shadow: inset 0px 0px 5px rgba(0,0,0,0.3); margin: 15px auto; height: 80px; border-radius: 5px; position: relative; cursor: pointer; } .player .volume-button { background: #00bdff; box-shadow: 0 0 20px rgba(0,0,0,0.3); border-radius: 30px; width: 20px; height: 20px; } .player .volume-button-holder { position: relative; top: -10px; } .player .volume-bar { background: #FF6600; border-radius: 5px; width: 100%; height: 100%; position: absolute; bottom: 0; } .player .fullscreen { width: 5%; cursor: pointer; float: left; height: 100%; } .player .fullscreen a { width: 25px; height: 20px; border-radius: 3px; background: #00bdff; display: block; position: relative; top: 10px; margin: 0px auto; } .player .fullscreen a:hover { background: #FF6600; } .player .volume-icon span { width: 20%; height: 23%; background-color: #00bdff; display: block; position: relative; z-index: 1; font-weight: bold; top: 40%; color: #fff; left: 22%; } .player .volume-icon span:before, .player .volume-icon span:after { content: ''; position: absolute; } .player .volume-icon span:before { width: 0; height: 0; border: 1em solid transparent; border-left: none; border-right-color: #00bdff; z-index: 2; top: -2px; left: 10%; margin-top: -40%; } .player .volume-icon span:after { width: 10%; height: 4%; border: 1px solid #00bdff; left: 150%; border-width: 0px 0px 0 0; border-radius: 0 50px 0 0; -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); -ms-transform: rotate(45deg); -o-transform: rotate(45deg); transform: rotate(45deg); font-variant: small-caps; } .player .v-change-11 span:after { border-width: 10px 10px 0 0; top: 0; } .player .v-change-10 span:after { border-width: 9px 9px 0 0; top: 1px; } .player .v-change-9 span:after { border-width: 8px 8px 0 0; top: 1px; } .player .v-change-8 span:after { border-width: 7px 7px 0 0; top: 2px; } .player .v-change-7 span:after { border-width: 6px 6px 0 0; top: 2px; } .player .v-change-6 span:after { border-width: 5px 5px 0 0; top: 3px; } .player .v-change-5 span:after { border-width: 4px 4px 0 0; top: 3px; } .player .v-change-4 span:after { border-width: 3px 3px 0 0; top: 4px; } .player .v-change-3 span:after { border-width: 2px 2px 0 0; top: 4px; } .player .v-change-2 span:after { border-width: 1px 1px 0 0; top: 5px; } .player .v-change-1 span:after { border-width: 0px 0px 0 0; top: 5px; } .player .v-change-1 span:after { content: '+'; -webkit-transform: rotate(45deg); font-size: 20px; top: -6px; left: 25px; color: #00bdff; } /* ------- IGNORE */ #header { width: 100%; margin: 0px auto; } #header #center { text-align: center; } #header h1 span { color: #000; display: block; font-size: 50px; } #header p { font-family: 'Georgia', serif; } #header h1 { color: #892dbf; font: bold 40px 'Bree Serif', serif; } #travel { padding: 10px; background: rgba(0,0,0,0.6); border-bottom: 2px solid rgba(0,0,0,0.2); font-variant: normal; text-decoration: none; } #travel a { font-family: 'Georgia', serif; text-decoration: none; border-bottom: 1px solid #f9f9f9; font-size: 20px; color: #f9f9f9; } .container { padding: 40px 0 0 0; } .logo { margin-top: 9px; float: left; margin-left: 6px; } JS (function($) { $.fn.videoPlayer = function(options) { var settings = { playerWidth : '0.95', // Default is 95% videoClass : 'video' // Video Class } // Extend the options so they work with the plugin if(options) { $.extend(settings, options); } // For each so that we keep chainability. return this.each(function() { $(this)[0].addEventListener('loadedmetadata', function() { // Basic Variables var $this = $(this); var $settings = settings; // Wrap the video in a div with the class of your choosing $this.wrap('<div class="'+$settings.videoClass+'"></div>'); // Select the div we just wrapped our video in for easy selection. var $that = $this.parent('.'+$settings.videoClass); // The Structure of our video player { $( '<div class="player">' +'<img class="logo" src="http://www.cpdonline.tv/ice-events/mediaplayer/icelogo.png" height="20px">' + '<div class="play-pause play">' + '<span class="play-button">►</span>' + '<div class="pause-button">' + '<span> </span>' + '<span> </span>' + '</div>' + '</div>' + '<div class="progress">' + '<div class="progress-bar">' + '<div class="button-holder">' + '<div class="progress-button"> </div>' + '</div>' + '</div>' + '<div class="time">' + '<span class="ctime">00:00</span>' + '<span class="stime"> / </span>' + '<span class="ttime">00:00</span>' + '</div>' + '</div>' + '<div class="volume">' + '<div class="volume-holder">' + '<div class="volume-bar-holder">' + '<div class="volume-bar">' + '<div class="volume-button-holder">' + '<div class="volume-button"> </div>' + '</div>' + '</div>' + '</div>' + '</div>' + '<div class="volume-icon v-change-0">' + '<span> </span>' + '</div>' + '</div>' + '<div class="fullscreen"> ' + ' ' + '</div>' + '</div>').appendTo($that); } // Width of the video $videoWidth = $this.width(); $that.width($videoWidth+'px'); // Set width of the player based on previously noted settings $that.find('.player').css({'width' : ($settings.playerWidth*100)+'%', 'left' : ((100-$settings.playerWidth*100)/2)+'%'}); // Video information var $spc = $(this)[0], // Specific video $duration = $spc.duration, // Video Duration $volume = $spc.volume, // Video volume currentTime; // Some other misc variables to check when things are happening var $mclicking = false, $vclicking = false, $vidhover = false, $volhover = false, $playing = false, $drop = false, $begin = false, $draggingProgess = false, $storevol, x = 0, y = 0, vtime = 0, updProgWidth = 0, volume = 0; // Setting the width, etc of the player var $volume = $spc.volume; // So the user cant select text in the player $that.bind('selectstart', function() { return false; }); // Set some widths var progWidth = $that.find('.progress').width(); var bufferLength = function() { // The buffered regions of the video var buffered = $spc.buffered; // Rest all buffered regions everytime this function is run $that.find('[class^=buffered]').remove(); // If buffered regions exist if(buffered.length > 0) { // The length of the buffered regions is i var i = buffered.length; while(i--) { // Max and min buffers $maxBuffer = buffered.end(i); $minBuffer = buffered.start(i); // The offset and width of buffered area var bufferOffset = ($minBuffer / $duration) * 100; var bufferWidth = (($maxBuffer - $minBuffer) / $duration) * 100; // Append the buffered regions to the video $('<div class="buffered"></div>').css({"left" : bufferOffset+'%', 'width' : bufferWidth+'%'}).appendTo($that.find('.progress')); } } } // Run the buffer function bufferLength(); // The timing function, updates the time. var timeUpdate = function($ignore) { // The current time of the video based on progress bar position var time = Math.round(($('.progress-bar').width() / progWidth) * $duration); // The 'real' time of the video var curTime = $spc.currentTime; // Seconds are set to 0 by default, minutes are the time divided by 60 // tminutes and tseconds are the total mins and seconds. var seconds = 0, minutes = Math.floor(time / 60), tminutes = Math.round($duration / 60), tseconds = Math.round(($duration) - (tminutes*60)); // If time exists (well, video time) if(time) { // seconds are equal to the time minus the minutes seconds = Math.round(time) - (60*minutes); // So if seconds go above 59 if(seconds > 59) { // Increase minutes, reset seconds seconds = Math.round(time) - (60*minutes); if(seconds == 60) { minutes = Math.round(time / 60); seconds = 0; } } } // Updated progress width updProgWidth = (curTime / $duration) * progWidth // Set a zero before the number if its less than 10. if(seconds < 10) { seconds = '0'+seconds; } if(tseconds < 10) { tseconds = '0'+tseconds; } // A variable set which we'll use later on if($ignore != true) { $that.find('.progress-bar').css({'width' : updProgWidth+'px'}); $that.find('.progress-button').css({'left' : (updProgWidth-$that.find('.progress-button').width())+'px'}); } // Update times $that.find('.ctime').html(minutes+':'+seconds) $that.find('.ttime').html(tminutes+':'+tseconds); // If playing update buffer value if($spc.currentTime > 0 && $spc.paused == false && $spc.ended == false) { bufferLength(); } } // Run the timing function twice, once on init and again when the time updates. timeUpdate(); $spc.addEventListener('timeupdate', timeUpdate); // When the user clicks play, bind a click event $that.find('.play-pause').bind('click', function() { // Set up a playing variable if($spc.currentTime > 0 && $spc.paused == false && $spc.ended == false) { $playing = false; } else { $playing = true; } // If playing, etc, change classes to show pause or play button if($playing == false) { $spc.pause(); $(this).addClass('play').removeClass('pause'); bufferLength(); } else { $begin = true; $spc.play(); $(this).addClass('pause').removeClass('play'); } }); // Bind a function to the progress bar so the user can select a point in the video $that.find('.progress').bind('mousedown', function(e) { // Progress bar is being clicked $mclicking = true; // If video is playing then pause while we change time of the video if($playing == true) { $spc.pause(); } // The x position of the mouse in the progress bar x = e.pageX - $that.find('.progress').offset().left; // Update current time currentTime = (x / progWidth) * $duration; $spc.currentTime = currentTime; }); // When the user clicks on the volume bar holder, initiate the volume change event $that.find('.volume-bar-holder').bind('mousedown', function(e) { // Clicking of volume is true $vclicking = true; // Y position of mouse in volume slider y = $that.find('.volume-bar-holder').height() - (e.pageY - $that.find('.volume-bar-holder').offset().top); // Return false if user tries to click outside volume area if(y < 0 || y > $(this).height()) { $vclicking = false; return false; } // Update CSS to reflect what's happened $that.find('.volume-bar').css({'height' : y+'px'}); $that.find('.volume-button').css({'top' : (y-($that.find('.volume-button').height()/2))+'px'}); // Update some variables $spc.volume = $that.find('.volume-bar').height() / $(this).height(); $storevol = $that.find('.volume-bar').height() / $(this).height(); $volume = $that.find('.volume-bar').height() / $(this).height(); // Run a little animation for the volume icon. volanim(); }); // A quick function for binding the animation of the volume icon var volanim = function() { // Check where volume is and update class depending on that. for(var i = 0; i < 1; i += 0.1) { var fi = parseInt(Math.floor(i*10)) / 10; var volid = (fi * 10)+1; if($volume == 1) { if($volhover == true) { $that.find('.volume-icon').removeClass().addClass('volume-icon volume-icon-hover v-change-11'); } else { $that.find('.volume-icon').removeClass().addClass('volume-icon v-change-11'); } } else if($volume == 0) { if($volhover == true) { $that.find('.volume-icon').removeClass().addClass('volume-icon volume-icon-hover v-change-1'); } else { $that.find('.volume-icon').removeClass().addClass('volume-icon v-change-1'); } } else if($volume > (fi-0.1) && volume < fi && !$that.find('.volume-icon').hasClass('v-change-'+volid)) { if($volhover == true) { $that.find('.volume-icon').removeClass().addClass('volume-icon volume-icon-hover v-change-'+volid); } else { $that.find('.volume-icon').removeClass().addClass('volume-icon v-change-'+volid); } } } } // Run the volanim function volanim(); // Check if the user is hovering over the volume button $that.find('.volume').hover(function() { $volhover = true; }, function() { $volhover = false; }); // For usability purposes then bind a function to the body assuming that the user has clicked mouse // down on the progress bar or volume bar $('body, html').bind('mousemove', function(e) { // Hide the player if video has been played and user hovers away from video if($begin == true) { $that.hover(function() { $that.find('.player').stop(true, false).animate({'opacity' : '1'}, 0.5); }, function() { $that.find('.player').stop(true, false).animate({'opacity' : '0'}, 0.5); }); } // For the progress bar controls if($mclicking == true) { // Dragging is happening $draggingProgress = true; // The thing we're going to apply to the CSS (changes based on conditional statements); var progMove = 0; // Width of the progress button (a little button at the end of the progress bar) var buttonWidth = $that.find('.progress-button').width(); // Updated x posititon the user is at x = e.pageX - $that.find('.progress').offset().left; // If video is playing if($playing == true) { // And the current time is less than the duration if(currentTime < $duration) { // Then the play-pause icon should definitely be a pause button $that.find('.play-pause').addClass('pause').removeClass('play'); } } if(x < 0) { // If x is less than 0 then move the progress bar 0px progMove = 0; $spc.currentTime = 0; } else if(x > progWidth) { // If x is more than the progress bar width then set progMove to progWidth $spc.currentTime = $duration; progMove = progWidth; } else { // Otherwise progMove is equal to the mouse x coordinate progMove = x; currentTime = (x / progWidth) * $duration; $spc.currentTime = currentTime; } // Change CSS based on previous conditional statement $that.find('.progress-bar').css({'width' : $progMove+'px'}); $that.find('.progress-button').css({'left' : ($progMove-buttonWidth)+'px'}); } // For the volume controls if($vclicking == true) { // The position of the mouse on the volume slider y = $that.find('.volume-bar-holder').height() - (e.pageY - $that.find('.volume-bar-holder').offset().top); // The position the user is moving to on the slider. var volMove = 0; // If the volume holder box is hidden then just return false if($that.find('.volume-holder').css('display') == 'none') { $vclicking = false; return false; } // Add the hover class to the volume icon if(!$that.find('.volume-icon').hasClass('volume-icon-hover')) { $that.find('.volume-icon').addClass('volume-icon-hover'); } if(y < 0 || y == 0) { // If y is less than 0 or equal to 0 then volMove is 0. $volume = 0; volMove = 0; $that.find('.volume-icon').removeClass().addClass('volume-icon volume-icon-hover v-change-11'); } else if(y > $(this).find('.volume-bar-holder').height() || (y / $that.find('.volume-bar-holder').height()) == 1) { // If y is more than the height then volMove is equal to the height $volume = 1; volMove = $that.find('.volume-bar-holder').height(); $that.find('.volume-icon').removeClass().addClass('volume-icon volume-icon-hover v-change-1'); } else { // Otherwise volMove is just y $volume = $that.find('.volume-bar').height() / $that.find('.volume-bar-holder').height(); volMove = y; } // Adjust the CSS based on the previous conditional statmeent $that.find('.volume-bar').css({'height' : volMove+'px'}); $that.find('.volume-button').css({'top' : (volMove+$that.find('.volume-button').height())+'px'}); // Run the animation function volanim(); // Change the volume and store volume // Store volume is the volume the user last had in place // in case they want to mute the video, unmuting will then // return the user to their previous volume. $spc.volume = $volume; $storevol = $volume; } // If the user hovers over the volume controls, then fade in or out the volume // icon hover class if($volhover == false) { $that.find('.volume-holder').stop(true, false).fadeOut(100); $that.find('.volume-icon').removeClass('volume-icon-hover'); } else { $that.find('.volume-icon').addClass('volume-icon-hover'); $that.find('.volume-holder').fadeIn(100); } }) // When the video ends the play button becomes a pause button $spc.addEventListener('ended', function() { $playing = false; // If the user is not dragging if($draggingProgress == false) { $that.find('.play-pause').addClass('play').removeClass('pause'); } }); // If the user clicks on the volume icon, mute the video, store previous volume, and then // show previous volume should they click on it again. $that.find('.volume-icon').bind('mousedown', function() { $volume = $spc.volume; // Update volume // If volume is undefined then the store volume is the current volume if(typeof $storevol == 'undefined') { $storevol = $spc.volume; } // If volume is more than 0 if($volume > 0) { // then the user wants to mute the video, so volume will become 0 $spc.volume = 0; $volume = 0; $that.find('.volume-bar').css({'height' : '0'}); volanim(); } else { // Otherwise user is unmuting video, so volume is now store volume. $spc.volume = $storevol; $volume = $storevol; $that.find('.volume-bar').css({'height' : ($storevol*100)+'%'}); volanim(); } }); // If the user lets go of the mouse, clicking is false for both volume and progress. // Also the video will begin playing if it was playing before the drag process began. // We're also running the bufferLength function $('body, html').bind('mouseup', function(e) { $mclicking = false; $vclicking = false; $draggingProgress = false; if($playing == true) { $spc.play(); } bufferLength(); }); // Check if fullscreen supported. If it's not just don't show the fullscreen icon. if(!$spc.requestFullscreen && !$spc.mozRequestFullScreen && !$spc.webkitRequestFullScreen) { $('.fullscreen').hide(); } // Requests fullscreen based on browser. $('.fullscreen').click(function() { if ($spc.requestFullscreen) { $spc.requestFullscreen(); } else if ($spc.mozRequestFullScreen) { $spc.mozRequestFullScreen(); } else if ($spc.webkitRequestFullScreen) { $spc.webkitRequestFullScreen(); } }); }); }); } })(jQuery); I would also like to point out that this is someone else source code and cannot find where i got this from. https://jsfiddle.net/f39huqpv/