Hi,

I am new to web development so I am still kind of unsure to how everything works so sorry if it is something obvious.

To create the scenario, I need to create a music player using html5, javascript and css. I very much got stuck on how to shuffle the songs that I have so that a random order is created and the previously played songs dont play after.

I have used a checkbox to create the shuffle 'button' which looks like this in html:

        <input type="checkbox" value="None" id="random_song" onclick="shuffleSong()" name="check" />
        <label class="random" for="random_song"></label>

and then I have created the following code in javascript to shuffle:

function shuffleSong() {
    const song = document.querySelector('#song')
    var shuffle = document.getElementById('random_song');
    var y = 0;
    song.addEventListener('ended', function () {
        if (shuffle.checked) {
            var x = Math.floor((Math.random() * 7));
            alert(x); // this is just for testing so I know which array element it will play

            song.src = songs[x];
            thumbnail.src = thumbnails[x];
            background.src = thumbnails[x];
            songArtist.innerHTML = songArtists[x];
            songTitle.innerHTML = songTitles[x];
            playing = true;
            playPause();;
        } else { //this works perfectly
            y++;
            if (y > 6) {
                y = 0;
            };
            song.src = songs[y];
            thumbnail.src = thumbnails[y];
            background.src = thumbnails[y];

            songArtist.innerHTML = songArtists[y];
            songTitle.innerHTML = songTitles[y];

            playing = true;
            playPause();
        }
    });
}

The following are arrays: song, thumbnail, background, songArtist and songTitle which of course contain all the relevant information for the songs that will be played.
The playPause() is a function to just automatically start playing the song as well if the play button is on.

Is there anyone who could tell me what I need to add to the code to eliminate the previously created random number?

Thank you so much in advance :)

You may be overthinking this. A simple solution would be to make an array with the usual 1 to N values (N being your max song count.) The array is shuffled and you play them one by one.

This meets what I read from you as the random criteria without a repeat pick.

But how exactly could I achieve this? Because I have several arrays which would need to be in sync so when I randomise this new array, how do I get the other arrays to play in that order?

You use the new shuffled array to provide N index values in random order. You use those index values to index into all the other arrays.
In other words - on line 7 above you set x to be the next value in the shuffled array of indexes.

commented: Could you please give me an example of how that would look? +0
commented: Yours was a much simpler solution! +8

The way I would do that is to have an array that stores the ID's of played songs then shuffle through finding the songs that have not played yet. I added the concept in jQuery as that is what I used to do it in my projects.

var played_songs = [];//array to store the ID's of played songs
function shuffleSong() {
    const song = document.querySelector('#song')
    var shuffle = document.getElementById('random_song');
    var y = 0;
    song.addEventListener('ended', function () {
        if (shuffle.checked) {
            var x = Math.floor((Math.random() * 7));
            while(jQuery.inArray(x, played_songs) !== -1 || played_songs.length == 7){// -1 means not found in the array, so loop until you find a song that has not played. 7 should be the amount of songs to play so it doesn't do an infinite loop
                x = Math.floor((Math.random() * 7));    
        }
            alert(x); // this is just for testing so I know which array element it will play

        played_songs.push(x);//add song ID to the played array
            song.src = songs[x];
            thumbnail.src = thumbnails[x];
            background.src = thumbnails[x];
            songArtist.innerHTML = songArtists[x];
            songTitle.innerHTML = songTitles[x];
            playing = true;
            playPause();;
        } else { //this works perfectly
            y++;
            if (y > 6) {
                y = 0;
            };
            song.src = songs[y];
            thumbnail.src = thumbnails[y];
            background.src = thumbnails[y];

            songArtist.innerHTML = songArtists[y];
            songTitle.innerHTML = songTitles[y];

            playing = true;
            playPause();
        }
    });
}

This will include jQuery <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> I would recommend downloading a copy and have it locally on your site incase an update breaks a script later on.

For best compatibility later I would use a unique song ID globally so it can fit with other widgets and things you make later on your site, like "Yellow" is song ID 1, "Adventure of a lifetime" is song ID 2. Then you can database it and get various elements on the site to link together.

You can do the same thing in normal javascript but I would get into jQuery cause it is a lot nicer and more efficient!

commented: Thank you so much! Unfortunately, I am required to use JavaScript for this project otherwise its not accepted.. +0

I have updated my code to the following:

function shuffleSong() {

    //Array Shuffle -- Fisher Yates Method
    function shuffleNumbers(songOrder) {
        var i = songOrder.length;
        if (i == 0) return false;
        while (--i) {
            var j = Math.floor(Math.random() * (i + 1));
            var tempi = songOrder[i];
            var tempj = songOrder[j];
            songOrder[i] = tempj;
            songOrder[j] = tempi;
        }
    }

    //Setting the array
    var currentSong = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    shuffleNumbers(currentSong);

    const song = document.querySelector('#song')
    var shuffle = document.getElementById('random_song');

    song.addEventListener('ended', function () {
        if (shuffle.checked) {
            for (x = 0; x < 10; x++) {
                alert("Next song is:" + currentSong[x]); //for testing

                song.src = songs[x];
                thumbnail.src = thumbnails[x];
                background.src = thumbnails[x];
                songArtist.innerHTML = songArtists[x];
                songTitle.innerHTML = songTitles[x];
                playing = true;
                playPause();
            }
        }
    });
}

However the issue now is that it will loop through the whole thing and not do it one by one..

I have done some research and I think a do .. while loop would work but I am unsure?

Does anyone have a better idea?

Fisher-Yates - very nice choice.

You should make a note of the different ways in which plays will be requested by the user ("Use Cases"), eg
start playing and continue until stopped
play one track and stop
start playing and play every track once
...

each of these will need a different kind of control structure.

My suggestion would be to refactor the code ito a playTheNextRandomTrack method that you can use in different ways, eg
event(play next) ->. playTheNextRandomTrack()
event(play everything) -> for i = 1 .. 10 playTheNextRandomTrack()
event(play forever) -> while not stop requested playTheNextRandomTrack()

I went through your code to get it working but I ended up having to rewrite it, I think you got a bit confused with the arrays and how things related to each other. If you save my example as a html file and look at how the functions relate you will see what I mean.

<script type='text/javascript'>
function shuffle(array) {//taken from https://bost.ocks.org/mike/shuffle/
  var m = array.length, t, i;

  // While there remain elements to shuffle…
  while (m) {

    // Pick a remaining element…
    i = Math.floor(Math.random() * m--);

    // And swap it with the current element.
    t = array[m];
    array[m] = array[i];
    array[i] = t;
  }

  return array;
}
var songPlaylist = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
songPlaylist = shuffle(songPlaylist);
var currentSongSelected = 0;
var songTitles = ['a','b','c','d','e','f','g','h','i'];

function shuffleSongs() {
    songPlaylist = shuffle(songPlaylist);
    document.getElementById('currentPlaylist').innerHTML = songPlaylist.toString();
}
</script>
<button onclick="currentSongSelected = 0;">Reset to first song</button>
<button id='shuffle_playlist' onclick="shuffleSongs();">Shuffle Songs</button>
<button id='song'>Test end song</button>
<ul>
<li id='message'></li>
<li id='currentPlaylist'></li>
<li id='songSrc'></li>
<li id='songThumb'></li>
<li id='songBG'></li>
<li id='songArtist'></li>
<li id='songTitle'></li>
</ul>

<script type='text/javascript'>
document.getElementById('song').addEventListener('click', function () {
    if(currentSongSelected == songPlaylist.length){
        return alert('no more songs');
    }
    if(typeof songTitles[currentSongSelected] != 'undefined'){
        document.getElementById('message').innerHTML = "Next song is:" + songTitles[currentSongSelected]; //for testing
    }else{
        document.getElementById('message').innerHTML = 'Last song';
    }
        document.getElementById('songSrc').innerHTML = 'songs['+songPlaylist[currentSongSelected]+'];'
    document.getElementById('songThumb').innerHTML = 'thumbnails['+songPlaylist[currentSongSelected]+'];'
    document.getElementById('songBG').innerHTML = 'thumbnails['+songPlaylist[currentSongSelected]+'];'
    document.getElementById('songArtist').innerHTML = 'songArtists['+songPlaylist[currentSongSelected]+'];'
    document.getElementById('songTitle').innerHTML = 'songTitles['+songPlaylist[currentSongSelected]+'];'
    currentSongSelected++;
    //playing = true;
    //playPause();
});
document.getElementById('currentPlaylist').innerHTML = songPlaylist.toString();
</script>
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.