jQuery problem referencing id - Using Howler.js - javascript

I have a maze I'm struggling to sort:
I have this grid with 12 anchor tags
for (let i = 1; i <= 12; i++) {
$("#minuetti").append(
`<div class="col-6 col-md-3"> <a id="minuetto${i}" class="btn btn-primary btn-lg mb-1 minuetto">Minuetto ${i}</a></div>`
);
}
Each one of those will play a different mp3 file with the code below using Howler.js.
$(".minuetto").on("click", function() {
let id = this.id;
let minuettoPath = `assets/music/${id}.mp3`;
let cell = new Howl({
src: [minuettoPath],
onplay: function() {
$(`#${id}`).text("Stop");
$(".minuetto").addClass("disabled");
$(`#${id}`).removeClass("disabled");
$(`#${id}`).removeClass("minuetto");
$(`#${id}`).on("click", function() {
cell.stop();
});
},
onend: function() {
$(".minuetto").removeClass("disabled");
$(`#${id}`).text(`Minuetto ${id.slice(8)}`);
$(`#${id}`).addClass("minuetto");
this.unload();
}
});
cell.play();
});
My problem:
I'm trying to change the text of the button clicked and keep it enabled.
I'm disabling all remaining (11) buttons until the end of the mp3 file.
But when I click on the enabled button (the one playing) instead of stopping my mp3, it restarts.
What am I doing wrong with my jQuery here? I'm referencing to my id and calling the function to do cell.stop() but for some reason jQuery runs the playing again, even though I removed the class minuetto from the playing button.
I'm really puzzled. Please help?

Use just const sound = new Howl({src: mp3.path});
Create a element using jQuery's Object Element constuctor $({})
Assign the needed events like click(), play(), and stop() that you can trigger when needed and perform your desired actions
Use Howler's sound.playing() to determine which trigger callback is needed
// Use an array of objects instead, otherwise if one day
// you decide to remove a numeric song you'll have to replace it with another.
// This also allows you to change the order of songs:
const mp3list = [
{name:'Enter Sandman', path:'https://upload.wikimedia.org/wikipedia/en/3/39/Metallica_-_Enter_Sandman.ogg'},
{name:'Back in Black', path:'http://upload.wikimedia.org/wikipedia/en/4/45/ACDC_-_Back_In_Black-sample.ogg',},
{name:'U2 - One', path:'https://upload.wikimedia.org/wikipedia/en/5/5e/U2_One.ogg',},
];
let $allMP3 = [];
const $newMP3 = mp3 => {
// HOWL
const sound = new Howl({src: mp3.path});
// $ ELEMENT
return $('<div>', {
class: 'minuetto',
text: mp3.name,
on: {
click() {
$allMP3.forEach($el => $el.not(this).trigger('stop'));
$(this).trigger(sound.playing() ? 'stop' : 'play');
},
play() {
$(this).text("Stop");
sound.play();
},
stop() {
$(this).text(mp3.name);
sound.stop();
}
}
});
};
$allMP3 = mp3list.map($newMP3); // Populate array of $ elements
$("#minuetti").append($allMP3); // Append once!
<div id="minuetti"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.1.3/howler.min.js"></script>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>

Related

Script affects all divs, how can I get it to affect just one?

I am using this function in NW.JS to get a file locations of images. I use that file location in the callback to modify a div background using .css() in jquery. My problem is that the script seems to remember the last div that it modified. When I try to use this to change the background of another div after previously having used it to change the background on a different div BOTH divs change their backgrounds. I guess I need to be able to get this script to know what button clicked it and to forget anything that another button asked it to do. As you can tell I am new to javascript. How can I do that?
function chooseFile(name, handleFile) {
var chooser = document.querySelector(name);
chooser.addEventListener("change", function(evt) {
for(var f of this.files){
console.log(f.name);
console.log(f.path);
handleFile(f.name, f.path);
}
}, false);
chooser.click();
}
chooseFile('#fileDialog', function(name, path){ ... /* do something with the file(s) */ });
In many cases, it’ll make sense to write your script so that it can react to new files:
const chooser = document.getElementById('fileDialog');
// Treat chooser as a stream of new files that can be added at any time
chooser.addEventListener("change", function (evt) {
for (const f of this.files) {
console.log(f.name);
console.log(f.path);
handleFile(f.name, f.path);
}
}, false);
// Then, when you want to prompt for a new file at any point in the future…
function promptForFiles() {
chooser.click();
}
When that’s not possible, you can instead have it hold a maximum of one handler at a time by assigning to the old but reliable onchange property:
function chooseFile(name, handleFile) {
const chooser = document.querySelector(name);
chooser.onchange = function () {
for (const f of this.files) {
console.log(f.name);
console.log(f.path);
handleFile(f.name, f.path);
}
};
chooser.click();
}

Get variables from button css in Javascript and use them as id

Hi I need a bit of help modifying my script. What I want to do:
I have a small and easy script. It changes the class of an container so I have influence on the behaviour and looking of the container. In my scenario the buttons open a div with a music player.
My problem is that I need to declare all buttons as a script. The button ID is in my case the onclick function (see code).
So when I have 10 or twenty links I need also everytime to modify the script. My idea is to have a script wich gets feed their variables by id's and classes of containers. So I need not to modify the script file.
// JavaScript Document
function AudioFF() {
var FFplayer = document.getElementById(x);
if (FFplayer.classList.contains("audio-hidden")) {
FFplayer.classList.remove("audio-hidden");
FFplayer.classList.add("audio-shown");
} else {
FFplayer.classList.remove("audio-shown");
FFplayer.classList.add("audio-hidden");
Array.prototype.slice.call(document.querySelectorAll('audio')).forEach(function(audio) {audio.pause();});
}
};
dbbtn.onclick = function() {
x = "deepblue";
AudioFF();
};
swbtn.onclick = function() {
x = "spacewalk";
AudioFF();
};
fbtn.onclick = function() {
x = "forest";
AudioFF();
};
drbtn.onclick = function() {
x = "dreamrhythm";
AudioFF();
};
My idea was to use the same class of a button as an id for the container who needs to fade in with a string. The button has e.g. the class btn_a, btn_b … etc. The containers has the id btn_a, btn_b … I wanted the script to catch the class of the button and use this classname as a variable for getElementById. The closebutton is also using the same script to close the container. Thanks for help :-)
I will recommend to use data attribute instead
example like this:
//register listener like this
var btns = document.querySelectorAll('[data-music]');
btns.forEach(function(elm) {
elm.addEventListener('click', function(e) {
//your function
console.log(this.dataset.music);
})
})
<!--your links-->
<div id="m1"></div>
<div id="m2"></div>
<div id="m3"></div>
<!--just add data-music attribute make it the same with your div id and all set-->
<button data-music="m1">play m1</button>
<button data-music="m2">play m2</button>
<button data-music="m3">play m3</button>
You should be able to set a data tag attribute to the buttons and just read the variable from that:
<button id="myButton" data="variableForMyButton" />
document.getElementById(myButton).onClick = function(e){
x = e.target.getAttribute('data')
}
If multiple params are required you add additional data tags:
<button id="myButton" data="variableForMyButton" data-action="someSweetAction" />
Thanks guys, that was what I was looking for. My function is now like this:
The play button and closebutton are working.
<button data-music="m1">Deep Blue</button>
<div id="m1">Container Content</div>
var btns = document.querySelectorAll('[data-music]');
btns.forEach(function(elm) {
elm.addEventListener('click', function(e) {
//function
var FFplayer = document.getElementById((this.dataset.music));
if (FFplayer.classList.contains("audio-hidden")) {
FFplayer.classList.remove("audio-hidden");
FFplayer.classList.add("audio-shown");
} else {
FFplayer.classList.remove("audio-shown");
FFplayer.classList.add("audio-hidden");
Array.prototype.slice.call(document.querySelectorAll('audio')).forEach(function(audio) {audio.pause();});
}
})
})
And here in jquery. Thanks to you all. You show me the way :-)
jQuery (document).ready(function($){
var btns = $('[data-music]');
$(btns).each(function() {
$('[data-music]').on('click', function(e) {
var FFplayer = $(this).data('music');
$("#" + FFplayer).toggleClass("audio-hidden audio-shown");
});
});
})

AngularJS Cordova Media Doesn't Always Play Next Song

I'm using the Ionic Framework with AngularJS and the ngCordova Media Plugin: http://ngcordova.com/docs/plugins/media/
I currently have two functions in my controller;
Play - this function plays the chosen media src
$scope.play = function(src) {
media = $cordovaMedia2.newMedia(src, null, null, mediaStatusCallback);
media.play();
$scope.isPlaying = true;
$ionicLoading.hide();
}
}
Next Song - this function gets the src of next song in the list by looking up the index of current song object and choosing the next object in the list.
$scope.nextSong = function (song_id) {
var index = $scope.allsongs.indexOf(song_id);
if(index >= 0 && index < $scope.allsongs.length - 1) {
$scope.nextsongid = $scope.allsongs[index + 1]
$scope.playMusic($scope.nextsongid); //the playMusic function just looks up the src of the song and parses to the play function
}
}
Here is the playMusic function which is called above:
$scope.playMusic = function(music_id)
{
myService.getSongInfo(music_id)
.then(function(data){
$scope.cursong = data.results;
$scope.play($scope.cursong.src);
});
};
And in my template, the next button just calls the nextSong function like so with the current song ID:
<div class="next" ng-click="nextSong('{{cursong.id}}')"></div>
The problem I'm facing is that when i press the next button first time, it plays the next song but when i press it again, it plays the same song again. It seems the 'currsong' value is not updated.
This is what the 'allsongs' array looks like:
["1631922", "1502059", "1365874", "1607940", "1251204", "1233296", "1151847", "1119737", "1086438", "1078140", "1068172", "1065312", "845339", "799161", "316293", "306073", "299817", "1603940"]
Update: I tried using an interval
I have an interval function which skips to next song when the time has reached the duration. I call the nextsong function there and it works perfectly.
$scope.Timer = $interval(function () {
$scope.cursongduration = $scope.cursong.duration;
$scope.cursongid = $scope.cursong.id;
if($scope.currduration >= $scope.cursongduration){
$scope.nextSong($scope.cursongid);
}, 1000);
};
So my question is why calling nextSong through the template button work as well? To me it seems like the cursong.id is not updated in the template after 1 time for some odd reason. I have also tried using cursongid variable which is set in the interval in the template button but no luck:
<div class="next" ng-click="nextSong('{{cursongid}}')"></div>
The problem isn`t in cordova or media plugin, when you play the song you have to update the $scope.cursong.id so the next click can know what is the current song.
$scope.play = function(src) {
$scope.cursong.id = src;
media = $cordovaMedia2.newMedia(src, null, null, mediaStatusCallback);
media.play();
$scope.isPlaying = true;
$ionicLoading.hide();
}
}
I`m assuming you have declared the $scope.cursong.id somewhere in your script.
I have resolved this issue by creating a new function 'PlayNextSong' which stores $scope.cursong.id into a new scope 'cursongid' and then calls the nextSong function. This works fine in my case.
The function:
$scope.playNextSong = function() {
$scope.cursongid = $scope.cursong.id;
$scope.nextSong($scope.cursongid);
}
The button:
<div class="next" ng-click="playNextSong()"></div>

JS/JQuery text to speech: How to chain audios?

Given n different words:
var text = [ "bèijing Beijing Shanghai"]
var words= [ "bèijing", "Beijing", "Shanghai" ];
Given n different .ogg audio files with a known locations :
<!-- AUDIOS FILES -->
<audio id="id1" src="/audio/Zh-bèijing.ogg"></audio>
<audio id="id2" src="/audio/Zh-Beijing.ogg"></audio>
<audio id="id3" src="/audio/Zh-Shanghai.ogg"></audio>
I use JS getElementById('...').play(), which I run using an onclick event over an HTML element:
<!-- HTML -->
<div id="play" onClick="document.getElementById('id1').play();">
<button>Play</button>
</div>
This allow me to play one audiofile.
Using one single onlick event, how play the first audio, then when done chain to it the play of a second audio ?
Starting fiddles with assets there : http://jsfiddle.net/J9wAB/
Note: I tried document.getElementById('id1').play().done(document.getElementById('id2').play()), but it plays both files simultaneously.
EDIT: I accepted one answer working on Firefox. Yet, this addEventListener based answer fails on on some browsers. An alternative promise base answer may be more successful.
Something like this :
$('#play').data('audio', $('audio:first')).on('click', function() {
var self = this;
$(this).data('audio').on('ended', function() {
var n = $('audio').eq( $(self).data('audio').index('audio') + 1 );
$(self).data('audio', n.length ? n : $('audio:first'));
}).get(0).play();
});
FIDDLE
It gets the first audio element in the document, and stores it in jQuery's data, and once it's played (the onended event) it get's the next audio element in the DOM and the next time the button is clicked, it playes that, and when there are no more audio elements, it starts from the beginning again.
If you really wan't to use the ID instead, it would be something like :
var audio = 1
$('#play').on('click', function() {
$('#id' + audio).get(0).play();
audio++;
});
FIDDLE
To play all three on a single click, one after the other, you'd do
$('#play').on('click', function() {
$('audio').on('ended', function() {
$('audio').eq($(this).index('audio')+1).get(0).play();
}).get(0).play();
});
FIDDLE
No jQuery needed, just listen for the ended event
The simple case is the following:
document.getElementById('id1').addEventListener('ended', function(){
document.getElementById('id2').play();
});
Abstracted, it would look like http://jsfiddle.net/J9wAB/13/
function chain(ids/* id, id, ... */) {
function setHandler(first, next) {
document.getElementById(first).addEventListener('ended', function(){
document.getElementById(next).play();
});
}
for (var i = 0; i < arguments.length - 1; i++) {
setHandler(arguments[i], arguments[i+1]);
}
}
chain('id1', 'id2', 'id3');
You can use the onended="yourcallback()" to start the next audio.
Look at this answer please: https://stackoverflow.com/a/7652194/2707424

Get numerical value from parent with id like 'post-1' and use it in jQuery function

I'm trying to figure out the following.
I have following jQuery code:
var as = "";
var bPlay = 0;
audiojs.events.ready(function() {
as = audiojs.createAll();
$(".audiojs .play-pause").click(function() {
var e = $(this).parents(".audiojs").index(".audiojs");
$.each(as, function(t, n) {
if (t != e && as[t].playing) {
as[t].pause()
}
})
bPlay = !bPlay;
if (bPlay == 1) {
$(".bar").each(function(i) {
fluctuate($(this));
});
} else {
$(".bar").stop();
}
})
});
In a nutshell it preforms list of things when someone clicks particular .audiojs instance on a page. 1) checks if there is any other instance playing, if there is pauses it. And if it is playing applies fluctuate function to elements on a page that have class="bar". This is the issue! I don't want to apply it to all .bar's on a page, but only to a specific group that is associated with particular .audiojs instance (the one that is being clicked and is playing).
I thought of the following solution. Each .audiojs instance is inside a div tag that has id like "post-1", "post-2" etc.. where numerical value is post id from database. I can add this numerical id to bar, so it would be like bar-1, bar-2 etc... However after this I'm having issues.
For javascript to work I need to retrieve numerical value from "post-[id]" associated with audiojs instance that is being clicked and than store it somehow, so I can use it like this afterwards
bPlay = !bPlay;
if (bPlay == 1) {
$(".bar-[value retrieved from post-...]").each(function(i) {
fluctuate($(this));
});
} else {
$(".bar-[value retrieved from post...]").stop();
}
Could someone explain to me how it can be achieved?
Honestly, the easiest way would be to stick it in a custom data-* attribute on the <div id="post-X"> element, like so:
<div id="post-1" data-bar="bar-1">...</div>
Then, you said your .audiojs element is inside that <div>, so just go from this inside the event handler to that <div> element (using .closest()) and get the value of it:
var barId = $(this).closest('[id^="post-"]').attr('data-bar');
Then when you need to use it:
$("." + barId).each(function(i) {
fluctuate($(this));
});
Instead of embedding the value in a class or ID, use a data-* attribute:
<div class="audiojs" data-fluctuate-target="bar-1">
<button type="button" class="play-pause">
<!-- ... -->
</button>
</div>
<div class="bar-1">
<!-- ... -->
</div>
In your click event handler, use the following to fluctuate or stop the correct elements:
var fluctuateClass = $(this).closest('.audiojs').attr('data-fluctuate-target');
$('.' + fluctuateClass).each(function () {
if (bPlay == 1) {
fluctuate($(this));
} else {
$(this).stop();
}
});

Categories

Resources