I am using a jQuery Based Memory Game on my site. You can restart the game at anytime you like. Works great. Now I finish a game then I hit Play Again. If I hit restart it no longer works. Why and How do I fix it?
Here is a fiddle.
JS:
// this script shows how you can get info about the game
var game = jQuery('div.slashc-memory-game'); // get the game
var info = jQuery('p#info').find('span'); // get the info box
var restart = jQuery('a#restart').css('visibility', 'visible'); // get the play again link
var playAgain = jQuery('a#play-again').css('visibility', 'hidden'); // get the play again link
// format time like hh:mm:ss
var formatTime = function(s)
{
var h = parseInt(s / 3600), m = parseInt((s - h * 3600) / 60); s = s % 60;
return (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
}
// listen for game 'done' event
game.bind('done', function(e)
{
// show basic stats
var stats = game.slashcMemoryGame('getStats');
info.html('Success ! Number of clicks : ' + stats.numClicks + ', elapsed time : ' + formatTime(parseInt(stats.time / 1000)) + '.');
playAgain.css('visibility', 'visible'); // show link
restart.css('visibility', 'hidden'); // show link
});
// Restart action
restart.click(function(e)
{
game.slashcMemoryGame('restart'); // restart game
});
// play again action
playAgain.click(function(e)
{
playAgain.css('visibility', 'hidden'); // hide link
info.html('Memory Game, click to reveal images. <a id="restart" href="#">Restart</a>'); // reset text
game.slashcMemoryGame('restart'); // restart game
e.preventDefault();
});
HTML:
<p id="info"><span>Memory Game, click to reveal images. <a id="restart" href="#">Restart</a></span> <a id="play-again" href="#">Play Again</a></p>
You're replacing the #restart element whenever you click #play-again at this line:
info.html('Memory Game, click to reveal images. <a id="restart" href="#">Restart</a>');
This means the click handler for the #restart element is no longer attached to the new element.
You should attach the handler to an ancestor element which is always present in the DOM using a delegated event handler .on():
info.on('click', '#restart', function(e)
{
game.slashcMemoryGame('restart'); // restart game
});
Fiddle
I replaced the #restart element's directly-bound event handler with a delegated event handler. Here's the reference for an extra read: Direct and delegated events. Basically, you can only bind event handlers to elements currently in the DOM, and:
Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time.
Related
I'm using Wavesurfer.js to visualize waveforms from . Is there a way to use jQuery or Javascript to dynamically change the loaded audio file when I click on an HTML element?
I'm able to visualize a single file, but don't know how to change the loaded file dynamically.
Here's the Wavesurfer.js instance in my footer
<script>
var wavesurfer = WaveSurfer.create({
backend: 'MediaElement',
container: '#waveform',
barWidth: 1,
barGap: 3,
cursorColor: '#e15a13',
cursorWidth: 3,
mediaControls: true,
hideScrollbar: true,
waveColor: "#000000",
fillParent: true,
responsive: true,
});
wavesurfer.setHeight(40);
wavesurfer.load('http://dev.chrislamdesign.com/shortwave/wp-content/uploads/2019/10/Pharrell-Williams-Happy-Official-Music-Video-1.mp3');
wavesurfer.on('ready', updateTimer)
wavesurfer.on('audioprocess', updateTimer)
// Need to watch for seek in addition to audioprocess as audioprocess doesn't fire
// if the audio is paused.
wavesurfer.on('seek', updateTimer)
function updateTimer() {
var formattedTime = secondsToTimestamp(wavesurfer.getCurrentTime());
$('.waveform-time-indicator .time').text(formattedTime);
}
function secondsToTimestamp(seconds) {
seconds = Math.floor(seconds);
var h = Math.floor(seconds / 3600);
var m = Math.floor((seconds - (h * 3600)) / 60);
var s = seconds - (h * 3600) - (m * 60);
h = h < 10 ? '0' + h : h;
m = m < 10 ? '0' + m : m;
s = s < 10 ? '0' + s : s;
return h + ':' + m + ':' + s;
}
</script>
I want to replace the loaded file (wavesurfer.load) with an HTML string on click. I'm using Wordpress to loop song results, each have a URL printed in a paragraph tag with the class "move_to_wavesurfer". So, when I click on the song, i want to replace the wavesurfer.load value with the paragraph tag value.
Here's the HTML to inject
<div class="col-md-3">
<a class="song-link" data-target="#song-8">
<img width="606" height="610" src="http://dev.chrislamdesign.com/shortwave/wp-content/uploads/2019/07/cover.png" class="attachment-full size-full wp-post-image" alt="" srcset="http://dev.chrislamdesign.com/shortwave/wp-content/uploads/2019/07/cover.png 606w, http://dev.chrislamdesign.com/shortwave/wp-content/uploads/2019/07/cover-150x150.png 150w, http://dev.chrislamdesign.com/shortwave/wp-content/uploads/2019/07/cover-298x300.png 298w" sizes="(max-width: 606px) 100vw, 606px"></a>
<p class="move_to_wavesurfer>http://dev.chrislamdesign.com/shortwave/wp-content/uploads/2019/10/other_song.mp3</p>
</div>
</div>
<p>In your PHP loop... maybe you can put the urls in a data attribute</p>
<button rel='song-switch' data-song-url='song-url-a'>
play
</button>
<button rel='song-switch' data-song-url='other-song-url'>
play
</button>
...
console.clear();
// I'd normally write this in plain JavaScript - but since you are using WordPress - it has jQuery already
function switchTrack(url) {
// your code...
alert('play the song ' + url);
}
// get references to the HTML(DOM) elements
var $switches = $('[rel="song-switch"]');
// add event listeners to each switch
$switches.each( function() {
// $(this) refers to each switch
$(this).on('click', function() {
var url = $(this).data('song-url'); // get the value of that attr
//
switchTrack(url); // pass it into your function - to load a new track
// and this is where you'd work with the player's api...
// consider wrapping your current code in a function - or breaking it into some reusable functions
});
});
// yes... you should use event delegation... but you can look that up in the future.
From what I can see of your code - you probably don't have a script file / or aren't using it - so, remember to put this JS in a script tag / or generally - just make sure it's in the right place for you.
Here's where you can learn more about jQuery and all of it's functions: https://jquery.com/
https://jsfiddle.net/sheriffderek/teLm7drn
and also... keep in mind that with WordPress... you'll need to wrap your jQuery code with something like this:
(function($) {
// $ Works! You can test it with next line if you like
// console.log($);
})( jQuery );
I have a Javascript function which are doing the following functions,
Hide a div content when a button click.
Getting a input time (h:m:s) from a html input field and countdown them.
Showing the counting result in a html p tag.
function countdownTimeStart() {
/* hide the timer panel div when start button click*/
document.getElementById('timer_panel',).
innerHTML=document.getElementById('time_count').innerHTML;
/* Start count the time in timer panel */
/* Start count the time in timer panel */
var time = document.getElementById("picker-dates").value;
time = time.split(':');
var date = new Date();
var countDownDate = date.setHours(time[0], time[1], time[2]);
var x = setInterval(function() {
// set hours, minutes and seconds, decrease seconds
var hours = time[0];
var minutes = time[1];
var seconds = time[2]--;
// if seconds are negative, set them to 59 and reduce minutes
if (time[2] == -1) {
time[1]--;
time[2] = 59
}
// if minutes are negative, set them to 59 and reduce hours
if (time[1] == -1) {
time[0]--;
time[1] = 59
}
// Output the result in an element with id="demo"
// add leading zero for seconds if seconds lower than 10
if (seconds < 10) {
document.getElementById("demo").innerHTML = hours + ": " + minutes + ": " + "0" + seconds + " ";
} else {
document.getElementById("demo").innerHTML = hours + ": " + minutes + ": " + seconds + " ";
}
// If the count down is over, write some text
if (distance < 0) {
clearInterval(x);
document.getElementById("demo").innerHTML = "00:00:00";
}
}, 1000);
}
<div id="timer_panel" class="timer_panel1>
<input type = " text " id = "picker-dates ">
<button id="start " onclick="countdownTimeStart(); ">
</div>
<div id="time_count " class="time_count " style="visibility:hidden;>
<p id="demo" class="count"></p>
</div>
Problem is the time counting result not showing inside of the "demo" p tag when hiding timer panel div. How can I solve this, can anyone help me !
I've noticed a few mistakes in your code, I'll try to outline them as clearly as I can for you.
HTML
Firstly in your HTML you haven't closed your input tags. Also there was a typo on your div with the id time_count that didn't have closed quotations.
As some other people have mentioned, you also had a typo in your id name.
But the biggest thing, is that your p tag is wrapped in the div that inline you have set to visibility:hidden. Your JS doesn't address this. Once I moved the p tag out from this div, I was able to see an output.
However...
JavaScript
The Javascript code has some minor adjustments too. Why do you create a date object that you don't use? I'd delete this is you don't need it.
I would also suggest you store your element for use later, rather than calling document.getElementById('demo') everytime you use it, like so:
var el = document.getElementById('demo');
How to stop once the timer reaches 0
I've added this logic to your if else block
if( seconds == 0 && minutes == 0 && hours == 0 ){
clearInterval(x);
el.innerHTML = "00:00:00";
}
It uses code that you were trying to implement earlier but wasn't quite right. Moving it to here should do the trick. Check out the codepen where I have updated the code also.
Cancel the timer
Firstly you'll need to add a new button to your HTML
<button id="cancel">Cancel</button>
Then within your setInterval function I've added the following code:
// select cancel button
var cancel = document.getElementById('cancel');
// set up cancel functionality
// create a function to handle the cancel
function cancelCountdown(){
el.innerHTML = "00:00:00";
clearInterval(x);
}
// attach listener to cancel
// if cancel button is clicked
cancel.addEventListener( 'click', cancelCountdown);
Pause the timer
Okay, so we need to add another button to your HTML with a id of pause like so:
<button id="pause" >pause</button>
Then we add this pause functionality just below the code we put
in to clear the timer.
// select the pause button
var pause = document.getElementById('pause');
// create pause function
function pauseCountdown(){
// grab the current time
let pauseTime = hours+":"+minutes+":"+seconds;
// set that time into the timeInput
// so that next time 'Start' is pressed
// the paused time will be used
let timeInput = document.getElementById('picker-dates');
timeInput.value = pauseTime;
// stop the time to 'pause'
clearInterval(x);
}
// add listener to catch the pause
pause.addEventListener('click' , pauseCountdown);
How does it work? Well we're kinda cheating. You can't pause an Interval timer (that I've been able to find anyway) but you can take
the values you're using for the time and store them. Then when the start button is pressed, the timer will start again with the paused interval time as the countdown time. I hope that makes sense. Check out the codepen to see the example working.
Hope this helps!
Cheers,
Your p element has id demo1, but your code uses demo.
I would like to create buttons that will play next/prev song on click, but they should seek the music on long press.
I did manage to change songs, but I couldn't find the seek methods. Also how to make it play from beginning?
And is there a way to determine how much have passed since the beginning of the song? I found that buffer have duration, but no sing of actual play time.
At init:
context = new (window.AudioContext || window.webkitAudioContext)()
To play song:
var buffer = song.buffer
var source = context.createBufferSource()
source.buffer = song.buffer
source.connect(analysers.main)
source.start(0, offset || 0)
Guess, you should use AudioBufferNode (it has ability to do seek) - https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode/start
Also, this wrapper might be usefull - https://github.com/eipark/buffaudio
Use taphold API to acheive the long press Event.
$(function(){
$( "your element" ).bind( "taphold", tapholdHandler );
function tapholdHandler( event ){
/*write your code here for seek forward */
}
});
Use JQuery Timer
var mousedowntime;
var presstime;
$("button[id$='" + buttonID + "']").mousedown(function() {
var d = new Date();
mousedowntime = d.getTime();
});
$("button[id$='" + buttonID + "']").mouseup(function() {
var d = new Date();
presstime = d.getTime() - mousedowntime;
if (presstime > 999/*You can decide the time*/) {
//Do_Action_Long_Press_Event();
}
else {
//Do_Action_Click_Event();
}
});
To Play from Beginning
function beginAudio(){
audio.trigger('pause');
audio.prop("currentTime",audio.prop("currentTime")-audio.prop("currentTime"));
audio.trigger('play');
}
or set current time to zero and then play.
For Forward and Backward use this
audio.prop("currentTime",audio.prop("currentTime")-5);// 5 secs backward
audio.prop("currentTime",audio.prop("currentTime")+5);// 5 secs forward
I have a range input in a HTML page, which actually stands for a music timeline. I got a script that moves the handle along the slider as the music progresses.
Now, I want to be able to move the handle so the music will play where I set the handle with the mouse.
The problem is that when I begin dragging the handle along the slider, as soon as its position is updated by the script, it is set to its actual position and I lose control over it.
How can I fix that so I can move my handle freely ?
To play the music I am using a plugin, soundmanager2, and I am using the provided whileplaying callback function to set the position of the slider as the music progresses.
Here is the HTML bit :
<div id="timeCtl">
<span id="timeRange">
<input id="time" type="range" value="0" min="0" max="1000"/>
</span>
<p id="timeCpt">00:00 / 00:00</p>
</div>
And the JS that goes with it :
//This is the callback function contained within an object being created
whileplaying : function() {
//This whole block processes the "position / duration" display
var dur = parseInt(this.duration/1000, 10);
var durMin = parseInt(dur/60, 10);
var durSec = dur%60;
var pos = parseInt(this.position/1000, 10);
var posMin = parseInt(pos/60, 10);
var posSec = pos%60;
if (posMin < 10)
{
posMin = "" + "0" + posMin.toString();
}
if (posSec < 10)
{
posSec = "" + "0" + posSec.toString();
}
if (durMin < 10)
{
durMin = "" + "0" + durMin.toString();
}
if (durSec < 10)
{
durSec = "" + "0" + durSec.toString();
}
var displayDur = durMin.toString() + ":" + durSec.toString();
var displayPos = posMin.toString() + ":" + posSec.toString();
g("timeCpt").innerHTML = displayPos + " / " + displayDur;
//And here is the part that take care of moving the handle
var curPos = parseInt(pos / dur * 1000, 10);
g("timeRange").innerHTML = "<input id=\"time\" type=\"range\" value=\"" + curPos.toString() + "\" min=\"0\" max=\"1000\">";
//The problem is that it moves the handle while I'm dragging it and then I lose control and have to grab it again
//How to avoid that ?
}
Thank you for your time reading this, and thank you in advance for your answers.
Actually I may have found a way to solve the problem.
I could make things so that when the onclick event is fired, the script moving the range is disabled, until the onrelease event is fired (totally not sure about the events names but I guess find out what they really are).
I'm gonna try this out and keep the thread up to date.
EDIT: This works.
Create a boolean variable.
Surround the updating part of your script with a condition controlled by the boolean variable.
Set the boolean variable to false when the slider's onmousedown event is fired, and it will prevent the script from updating it.
Set it back to true when onmouseup is fired, with some additionnal processing if needed.
I'm making a webpage where user events are logged in.
To test the feature I made a small, independant webpage with a teaxtarea and a text input. The events logged are those performed on the input element.
I want to prevent the same event text to be shown multiple times in a row, but I can't seem to prevent them from showing up!
I also want to add a line to separate event groups 0.5 seconds after no other event happened, but the line seems to appear on every event trigger, evenif I use clearTimeout with the timeout ID.
Basically: I don't want any line to be repeated. If the last line is a separator line, then it must not add another one. Yet it doesn't see to work.
JSFiddle Demo
Here is my code:
JavaScript
var timerID = 0;
function addSeparateLine()
{
document.getElementById('listeEvenements').value += "--------------------\n";
}
function show(newEventText)
{
var eventListField = document.getElementById('listeEvenements');
var eventList = [];
if (eventListField.value.length > 0)
{
eventList = eventListField.value.split("\n");
}
var eventCounter = eventList.length;
if (eventList[eventCounter - 2] == newEventText)
{
clearTimeout(timerID);
newEventText = "";
}
timerID = setTimeout(addSeparateLine, 500);
if (newEventText !== "")
{
eventListField.value += newEventText + "\n";
}
return true;
}
HTML
<fieldset id="conteneurLogEvenements">
<legend>Events called from HTML attribute</legend>
<textarea id="listeEvenements" rows="25"></textarea>
<input id="controleEcoute" type="text" onBlur="show('Blur');" onchange="show('Change');" onclick="show('Click');" onfocus="show('Focus');" onMousedown="show('MouseDown');" onMousemove="show('MouseMove');" onMouseover="show('MouseOver');" onkeydown="show('KeyDown');"
onkeypress="show('KeyPress');" onkeyup="show('KeyUp');" />
</fieldset>
http://jsfiddle.net/z6kb4/2/
It sounds like what you want is a line that prints after 500 milliseconds of inactivity, but what your code currently says to do is "print a line 500 milliseconds after any action, unless it gets canceled". You can get better results by structuring the code more closely to your intended goal.
Specifically, instead of scheduling a new timeout every time an event occurs, simply start a loop when the first event occurs that checks the time that has elapsed since the most recent event received and then prints a line when the elapsed time exceeds the desired threshold (500 milliseconds). Something like:
function addSeparateLine() {
var elapsed = new Date().getTime() - lastEventTime;
if (elapsed >= 500) {
document.getElementById('listeEvenements').value += "--------------------\n";
clearInterval(timerID);
timerID = -1;
}
}
...and then you schedule it like:
if(newEventText !== "") {
lastEventTime = new Date().getTime();
eventListField.value += newEventText+"\n";
if (timerID == -1) {
timerID = setInterval(addSeparateLine,100);
}
}
Working example here: http://jsfiddle.net/z6kb4/4/
Because you are not actually stopping the show function in any way. The clearTimeout only applies to the separator add. I have updated your fiddle. You need to wrap your function with
if (+new Date() - lastfire < 500) return;
and
lastfire = +new Date();
(before the last return--see the updated fiddle). Also, make sure to stick the global definition var lastfire = -1; somewhere up top.