toggle button not working properly for audio. also javascript code only playing one audio element

Problem

I have tried for hours to get this working but I am new to javascript so can't seem to solve it. There are two audio tags altogether in the html code below with two different mp3 files. I have two questions here: 1. When I click on the label with class="play-bt" the audio does not play. It only plays if I click near the label to the left hand top corner of the containing element. I would like it to start play when I click anywhere on the label. I have tried putting "display: block;" in my css code in the label definition but this did not work.
2. When I press "near the label" of the second audio element it plays the audio of the first audio element. I'm wondering how I can edit the javascript code so that it can play multiple audio files. Thanks for the help. Here is my html code:

<ul id="audio_list">

<li>


    <div class="audio_controls gradient_dblue">
        <div class=circle></div>
        <div class="switch play_button">
            <audio class="audio-player"> 
                <source src="music/electronic beat-monksee.mp3" type="audio/mpeg" />
                        <source src="music/electronic beat-monksee.mp3" type="audio/mpeg" />
                        Your browser does not support HTML5 audio.
                    </audio>

            <input type="checkbox">
            <label class="play-bt"><i class='icon-play fade'></i><i class='icon-stop fade'></i></label>

        </div>

    </div>
    <div class="audio_description">Electro sounds</br><div class="artist">Sarah monks</div></div>

</li>



<li>
    <div class="audio_controls gradient_dblue">
        <div class=circle></div>
        <div class="switch play_button">
            <audio class="audio-player"> 
                <source src="music/dubstep.mp3" type="audio/mpeg" />
                        <source src="music/dubstep.mp3" type="audio/mpeg" />
                        Your browser does not support HTML5 audio.
                    </audio>

            <input type="checkbox">
            <label class="play-bt"><i class='icon-play fade'></i><i class='icon-stop fade'></i></label>

        </div>

    </div>
    <div class="audio_description">Dubstep Beat</br><div class="artist">Sarah monks</div></div>


</li>



</ul>

here is my javascript code:

$(document).ready(function(){
    $(".play-bt").click(function(){
        $(".audio-player")[0].play();
        $("#message").text("Music started");
    })

    $(".pause-bt").click(function(){
        $(".audio-player")[0].pause();
        $("#message").text("Music paused");
    })

    $(".stop-bt").click(function(){
        $(".audio-player")[0].pause();
        $(".audio-player")[0].currentTime = 0;
        $("#message").text("Music Stopped");
    })
})

here is my html code simplified a bit:

<ul id="audio_list">

<li>


    <div class="audio_controls">
        <div class="switch play_button">
            <audio class="audio-player"> 
                <source src="music/electronic beat-monksee.mp3" type="audio/mpeg" />
                    </audio>

            <input type="checkbox">
            <label class="play-bt"><i class='icon-play fade'></i><i class='icon-stop fade'></i></label>

        </div>

    </div>
    <div class="audio_description">Electro sounds</div>

</li>



<li>
    <div class="audio_controls">
        <div class="switch play_button">
            <audio class="audio-player"> 
                <source src="music/dubstep.mp3" type="audio/mpeg" />
                    </audio>

            <input type="checkbox">
            <label class="play-bt"><i class='icon-play fade'></i><i class='icon-stop fade'></i></label>

        </div>

    </div>
    <div class="audio_description">Dubstep Beat</div>   

</li>

</ul>

Edit: This is my while loop which loops through all of the rows of my database

<?php
$host = "localhost"; 
$user = ""; 
$pass = ""; 
$db = ""; 

$con=mysqli_connect($host,$user,$pass,$db);
        // Check connection
        if (mysqli_connect_errno())
            {
            echo "Failed to connect to MySQL: " . mysqli_connect_error();
            }

            $result = mysqli_query($con,"SELECT * FROM products");
            if (!$result) {
                printf("Error: %s\n", mysqli_error($con));
                exit();
            }
            echo "<ul id='audio_list'>";

        while($row = mysqli_fetch_array($result))
        {
            ?>
        <li>
        <div class='audio_controls_area'>
            <div class='play_button'>

                <input class="controler_btn click_play" type='checkbox'>
                <label class='play-bt'><i class='icon-play fade'></i><i class='icon-stop fade'></i></label>
                <audio class='audio-player'> 
                    <source src=<?php echo $row['Productmp3']; ?> type='audio/mpeg' />
                            <source src=<?php echo $row['Productmp3']; ?> type='audio/ogg' />
                    <object type='audio/x-mpeg' width='300px' height='45px' data=<?php echo $row['Productmp3']; ?> >
                        <param name='src' value=<?php echo $row['Productmp3']; ?> />
                        <param name='autoplay' value='false' />
                        <param name='autostart' value='false' />
                    </object>
                        </audio>

            </div>

        </div>
        <div class='audio_description'><?php echo $row['ProductName']; ?>
        <br>
        <div class="message"></div>
        <div class="artist"><?php echo $row['ProductArtist']; ?></div>
        </div>

        </li>
         <?php
        }
        echo "</ul>";
        mysqli_close($con);
        ?>

I got help the next day after I posted this with jquery code that would allow only one song play at a time and that could play and stop a song as required.

$(document).ready(function(){
    function stop_all (ele) {
        console.log(ele + "in funct");
        //console.log(ele.parent().parent().parent().siblings().find(".audio-player").length);
        var list = ele.parent().parent().parent().siblings().find(".audio-player");
        console.log(list.length);
        list.each(function(){
            $(this).siblings(".controler_btn").prop('checked', false).removeClass("click_stop").addClass("click_play");
            //alert($(this).siblings(".controler_btn"));
            console.log("loop");
            $(this)[0].pause();
            $(this)[0].currentTime = 0;
        });
    }
        $(".controler_btn").click(function(){
            if ($(this).hasClass("click_play")) {
                stop_all($(this));
                console.log($(this).parent().parent().parent().siblings().find(".audio-player"))
                console.log("playing");
                $(this).removeClass("click_play").addClass("click_stop");
                $(this).siblings(".audio-player")[0].play();
                $(this).parent().parent().siblings(".audio_description ").find(".message").show().html("Music started").hide(3000);
                //$(".audio-player")[0].play();
                //$("#message").text("Music started");
            }
            else if ($(this).hasClass("click_stop")) {
                console.log("stopped");
                $(this).removeClass("click_stop").addClass("click_play");
                $(this).siblings(".audio-player")[0].pause();
                $(this).siblings(".audio-player")[0].currentTime = 0;
                //$(".audio-player")[0].pause();
                //$(".audio-player")[0].currentTime = 0;
                $(this).siblings(".message").show().text("Music Stopped").hide(3000);
                //$("#message").text("Music Stopped");
            }

        })
    })

Thanks to @kuroi neko for your advice. I see what you mean about the resources. This code works great but I would like to edit it as you said with the for loops and the id on the audio players as the site is really for mobile so I would like to make it more practical. I'm wondering in the php code would I need to put the for loop inside the while loop or have only a for loop instead? Thanks

Problem courtesy of: Sarah

Solution

This is no JavaScript. This is the dreaded JQuery, that allows you to write inefficient and obfuscated code that takes hours to debug :).

Seriously, querying all audio player divs each time you move a matchstick is terribly inefficient. In that case it does not matter because it's just a user interface, but doing that for each paragraph of your document would make your code crawl.
(taking a French leave before being skinned alive by the JQuery crowd...)

problem 1

What you ask JQuery to do is put a click handler on your 3rd div. It does just that, so you can only generate a click event if you manage to put your mouse cursor on the said div.

Without any stylesheet, your div happens to have a height of 0 pixels, which makes it hard to locate on the screen. I suppose you did uses some CSS, but my guess is, it was not good enough to make your play button stand out.

I did run your code with the following CSS :

.play-bt { display:inline-block; background:red; width: 30px; height:30px;}

and the play button acquired a height and became a responsive big red square :)

problem 2

You hard-coded your click handler to use the 1st div that happens to have a play-bt CSS class, so wherever you click, the 1st song will be played:

$(".audio-player")[0].play(); // this "0" selects the 1st audio player

To avoid that, you have to associate each play/stop/pause button with the appropriate audio div.


Edited after Sarah's remarks

the famous button

It appears you styled the checkbox (very nicely, by the way), so I assume the checkbox is the intended button. But your JQuery goocode is aiming at the div just next to it :)
Move the bn-play CSS class from this div to your checkbox and Bob should be your uncle.

pairing buttons with the proper audio player

There are many possibilities to achieve this.

Since you will use PHP to generate the HTML, I would suggets attributing a unique ID to each of your audio div, and use it to query the proper audio from within your event handlers.

Very schematic PHP code:

$audio_id = 0; // unique ID for each mp3 player

// part of the HTML generation
foreach ($songs as $song)
{
    echo "<audio id='audio_" . $audio_id . "'>"; // generate a unique ID
    $audio_id++;                                 // for each audio block
}

Even more schematic JQuery g... code

for (var i = 0 ; i != number_of_audio_files_on_the_page ; i++)
{
    $(".play-bt").click(function(){
        $("#audio_" + i).play();         // reference the audio_xxx unique id
        $("#message").text("Music started");
    })

   // same trick for other buttons
}

Note that this code will not work out of the box: you will need a closure on the loop index. But this another story.

more on resources consumption

Having a different audio player for each song is both wasteful and impractical, IMHO.

It is wasteful because (especially if you allow auto-buffering), each MP3 file will be downloaded in parallel, even if the user never plays it. The browser will permanently allocate a few megabytes of MP3 data for each song on the page.

As for practicality, imagine what will happen if the user starts playing a song and then clicks another "play" button...
Unless real-time remixes are a lead feature of your site, you will want to prevent the user from inadvertently playing more than one song at a time :).

To kill the two birds, a possible single stone would be to create a single audio player, and change the source according to the last "play" button pressed.

This could even lead to a redesign of your interface, since there would be no need for a pause or stop button next to each song.

You could imagine a "select" button next to each song (that would also stop the current song and start the newly selected one), and another control pannel in the header or footer of the page with more sophisticated controls acting on the current song (a seek bar for instance).

a little update

This is a true eyesore that can turn into a headache any time:

var list = ele.parent().parent().parent().siblings().find(".audio-player");

One of the many drawbacks of JQuery is that it makes much too easy to navigate between DOM elements. It means your code tends to become dependent on the structure of the document.

Functionnally, you don't care where the audio players and all the buttons are located. You should only access them by class names or IDs.

Using this code, you must know that the audio players are the fore-fore-fore-fathers of whatever element you were playing with at the time you decided to access them, the play button is the second cousin of the seekbar, etc.
It means you will have to tweak your code each time you want to change the page layout. Very impractical, IMHO.

My advice would be to write a function that handles start/stop/pause for a given audio player, and then call that function at the appropriate time:

  • on the current song to play it, and on the others to stop them
  • on the current song to pause or stop it.

So I cannot really answer your question about for/while loops. I think a more global code refactoring would be a better investment to keep your design light and evolutive.

Solution courtesy of: kuroi neko

Discussion

There is currently no discussion for this recipe.

This recipe can be found in it's original form on Stack Over Flow.