views:

90

answers:

3

I have a set of images that correspond to video thumbnails. The user clicks a thumb which loads the browser. This would be simple enough, but I need to track which of the thumbs was clicked, so that I can automatically cue up the next video in sequence.

My first thought was to do something like this (highly simplified example):

<div class="thumbs">
<img id="vt_0" src="thumbxxx00.jpg" />
<img id="vt_1" src="thumbxxx01.jpg" />
<img id="vt_2" src="thumbxxx02.jpg" />
<img id="vt_3" src="thumbxxx03.jpg" />
<img id="vt_4" src="thumbxxx04.jpg" />
<img id="vt_5" src="thumbxxx05.jpg" />
<img id="vt_6" src="thumbxxx06.jpg" />
</div>

<script type="text/javascript">
var videos = [ "xxx00", "xxx01", "xxx02", "xxx03", "xxx04", "xxx05", "xxx06" ];
var video_index = null;

function playVideo(id) {
// play video then call "onVideoFinish()" when video ends. 

}

function onVideoFinish() {
    video_index = (video_index = 6) ? video_index : video_index+1;
    playVideo(videos[video_index]);
}

    $j("div.thumbnail img").live("click", function (e) {
      e.preventDefault();
      var selected_id = $(this).attr("id").split("_")[1];
      video_index = selected_id;
      playvideo( videos[video_index] );
    });

</script>

At first glance this seems to be okay, but I'm not sure if this is the best/most elegant solution, especially as I'd be implementing all these methods from within an object context.

A: 

Here's how I would do it.

Instead of storing the video names inside an array, why not store the video name along with the thumbnail? You can use the class attribute to store the name of the video.

Once you take this approach, things become simple.

<div class="thumbs">
<img id="vt_0" src="thumbxxx00.jpg" class="xxx00"/>
<img id="vt_1" src="thumbxxx01.jpg" class="xxx01"/>
<img id="vt_2" src="thumbxxx02.jpg" class="xxx02"/>
<img id="vt_3" src="thumbxxx03.jpg" class="xxx03"/>
<img id="vt_4" src="thumbxxx04.jpg" class="xxx04"/>
<img id="vt_5" src="thumbxxx05.jpg" class="xxx05"/>
<img id="vt_6" src="thumbxxx06.jpg" class="xxx06"/>
</div>

<script type="text/javascript">

    function setupVideoPlayer(order)
    {
        //lastThumb won't be accessible from outside of setupVideoPlayer 
        //function.
        var lastThumb = null;
        var imageSelector = 'div.thumbs img';

        function playVideo(video)
        {
            //Play the video.
            onVideoFinish();
        }

        function onVideoFinish()
        {
            //If order is 'ascending', we will go to the 'next'
            //image, otherwise we will go to 'previous' image.
            var method = order == 'asc' ? 'next' : 'prev';

            //When user is at the end, we need to reset it either at the 
            //first image (for ascending) or the last (for descending). 
            var resetIndex = order == 'asc' ? 0 : $(imageSelector).length - 1;

            //When video has finished playing, we will try to 
            //find the next/prev (depending upon order) sibling of 'lastThumb', 

            //If we can not find any sibling, it means we are at the
            //last/first thumbnail and we will go back and fetch the first/last
            //image. 

            //Also, instead of calling the playVideo method, we will
            //fire the click event of thumbnail. This way, if you decide to
            //do something in future (say playing an ad before the video)
            //you only need to do it in your click handler.            

            if($(lastThumb)[method]().length == 0)
                $(imageSelector).get(resetIndex).click();
            else
                $(lastThumb)[method]().click(); 

        }

        $j(imageSelector)
            .click(
                function()
                {
                    //on click, we store the reference to the thumbnail which was 
                    //clicked.
                    lastThumb = this;

                    //We get the name of the video from the class attribute
                    //and play the video. 
                    playVideo($(this).attr('class'));
                }
              );
    }

    $(document).ready(
        function() { setupVideoPlayer('asc'); }
    );

</script>

Above code has the advantage that you can modify your HTML and it will automatically play those videos.

SolutionYogi
I was thinking about this too, the only issue is that it's possible I'd want to play things in reverse order (in which case I'd be decrementing and not incrementing). You can't really look for siblings behind you AFAIK, so I guess maintaining an array is the better approach after all.
FilmJ
There is a 'prev' method too. So you can use this to go in reverse order.
SolutionYogi
I updated my original answer to take 'order' argument, it could be 'asc' or 'desc'.
SolutionYogi
A: 

You can wrap the images with an anchor tag and use the onClick method as follows:

<a href="java script:;" onClick="playVideo(0)"><img src="thumbxxx00.jpg" /></a>
<a href="java script:;" onClick="playVideo(1)"><img src="thumbxxx01.jpg" /></a>
...
ZeroConcept
-1. If you are using jQuery, there is no reason to put event handlers in HTML. You should hook them from the onready/onload events.
SolutionYogi
There's no reason to put event handles in HTML regardless of what library you're using, or even if you're not using one at all.
Justin Johnson
+1  A: 

This is how I would do it. The only global that you need in this case is currentPlayOrder, which could be stored someone as part of a preferences or configuration model.

First the HTML. I moved the video sources into the rel attribute of the associated thumbnail. I assume that your application is generating the thumbnails, in which case, this would be an appropriate method since whatever generates the thumbnail HTML could be made aware of the associated video sources.

<div class="thumbs">
    <img id="vt_0" src="http://stackoverflow.com/content/img/so/logo.png" rel="videoA"/>
    <img id="vt_1" src="http://serverfault.com/content/img/sf/logo.png"   rel="videoB"/>
    <img id="vt_2" src="http://stackoverflow.com/content/img/so/logo.png" rel="videoC"/>
    <img id="vt_3" src="http://serverfault.com/content/img/sf/logo.png"   rel="videoD"/>
    <img id="vt_4" src="http://stackoverflow.com/content/img/so/logo.png" rel="videoE"/>
    <img id="vt_5" src="http://serverfault.com/content/img/sf/logo.png"   rel="videoF"/>
    <img id="vt_6" src="http://stackoverflow.com/content/img/so/logo.png" rel="videoG"/>
</div>

Now the JS. Notice the use of previousSibling and nextSibling to determine play order:

<script type="text/javascript">

var PLAY_ORDER_BACKWARD = "previousSibling";
var PLAY_ORDER_FORWARD  = "nextSibling";

var currentPlayOrder = PLAY_ORDER_FORWARD;

$(document).ready(function() {
    $(".thumbs img").each(function(i, node) {
     $(node).click(function() {
      playVideo(this.getAttribute("rel"), this);
     });
    });
});

var playVideo = function(source, thumbNode) {
    console.log("Play video %s", source);
    onVideoFinish(thumbNode);
    // If your video play accepts a callback, you may need to pass it as
    // function() { onVideoFinish(thumbNode); }
}

var onVideoFinish = function(thumbNode) {
    // Get the next img node (if any) in the appropriate direction
    while ( thumbNode = thumbNode[currentPlayOrder] ) {
     if ( thumbNode.tagName == "IMG" ) { break; }
    }

    // If an img node exists and it has the rel (video source) attribute
    if ( thumbNode && thumbNode.getAttribute("rel") ) {
     playVideo(thumbNode.getAttribute("rel"), thumbNode);
    }
    // Otherwise, assume that there are no more thumbs/videos in this direction
    else {
     console.log("No more videos to play");
    }
}
</script>

Hope that helps.

Justin Johnson