Home >
Last week I began a series detailing the creation of a sports tournament style brackets picker built with jQuery. As I wrapped up the previous entry, I had the basic display done, and had made use of the Selectable interaction library from jQuery UI to enable selection of winning teams in each game. However, I did not have a way to handle unique selections and I didn't have a way of noticing when you had picked all the winners. Today's entry will correct both problems. Be sure to read the earlier post (or at least the complete source of the demo) or a lot of the following won't make much sense.
First let's talk about the single selection problem. If you take a quick peek at the first demo, notice that nothing prevents from picking both teams as a winner in each game. While that would certainly make for some safe bets in Vegas, it doesn't help us get to to a final winner in the bracket. I did some digging into the Selectable docs and was not able to find a way to enable single selection. I found this listserv posting where Richard Worth suggested a much simpler way to handle single selection.
So here was the old code that enabled the Selectable interaction for each game:
and I've now replace it with:
As you can see, I switched Richard's method. I've also added a checkComplete() call. This is what will handle seeing if the round is complete. Let's talk a bit about that as it was a somewhat of a pain to handle. The idea is simple enough, right? We have N games. Each game consists of two teams that where we know that at most one can be selected at a time. Our "rule" then should be simple - if each pair has one item selected, then the round is complete. I'll show the code I came up with and then discuss it a bit more in depth.
First, we grab a pointer to the current round. I added a variable to my code to store the current round so this is globally available. Next I set a simple boolean value, allGood, that I'll use as a flag to see if the round is done. From the previous blog entry we know that a round is an array of games. I loop over each item in the array and get a count of the number of items with the selected class:
This was the first time I had used the size() attribute and, honestly, the first time I had need of it. As you can guess, it simply gives you a count of the number of items matched by your selector. Each round is a UL wrapped by a div named round_X where X is a the round number. So the selector says: Within round X, game i, count the LI tags with the selected class. If the matched size is not 1 we can set allGood to false and break out of the loop.
If the matched size is 1, notice I then grab the selected item, which is the winning team, and begin to parse data about it. I store the teams ID value (which if you remember from the first entry would have come from the database) and the teams name. This gets added to a new array called winningTeams.
The last thing we do is loop over the winningTeams array and add it as a new round. Once we've created new games from the winning teams, we can call drawGames() again. The end result is that we can now pick winners until we run out of teams to pick!
You can view a demo of this here. It's definitely moving along now but we have two final problems. First, if you do indeed get to the end you will notice a JavaScript error. The code simply can't handle running out of rounds. Second, once we do get to the end, we need to serialize the results. What teams you picked and at what round. Once serialized we could send it to the server for storage. There are a few other things we should look at as well. You can continue to click teams in earlier rounds, but doing so doesn't impact the later rounds. It would be easiest probably to just remove the toggle feature once you've completed a round. We should also look at improving the UI a bit so each round is a bit more distinct.
Thoughts so far?
First let's talk about the single selection problem. If you take a quick peek at the first demo, notice that nothing prevents from picking both teams as a winner in each game. While that would certainly make for some safe bets in Vegas, it doesn't help us get to to a final winner in the bracket. I did some digging into the Selectable docs and was not able to find a way to enable single selection. I found this listserv posting where Richard Worth suggested a much simpler way to handle single selection.
$("#myList > *").click(function() {
$(this).toggleClass("selected").siblings().removeClass("selected");
});
So here was the old code that enabled the Selectable interaction for each game:
for(var i=0;i<round.length;i++) {
$("#selectable_"+i).selectable()
}
and I've now replace it with:
for(var i=0;i<round.length;i++) {
$("#selectable_"+i+" > li").click(function() {
$(this).toggleClass("selected").siblings().removeClass("selected")
checkComplete()
});
}
As you can see, I switched Richard's method. I've also added a checkComplete() call. This is what will handle seeing if the round is complete. Let's talk a bit about that as it was a somewhat of a pain to handle. The idea is simple enough, right? We have N games. Each game consists of two teams that where we know that at most one can be selected at a time. Our "rule" then should be simple - if each pair has one item selected, then the round is complete. I'll show the code I came up with and then discuss it a bit more in depth.
function checkComplete() {
//get all the games at this round
var round = rounds[currentRound]
var allGood = true
var winningTeams = []
for(var i=0;i<round.length;i++) {
//for each, if both aren't selected, allGood is false
var totalselected = $("#round_"+currentRound+" #selectable_"+i+" > li.selected").size()
if(totalselected != 1) {
allGood=false
break
} else {
//this is the id of selected team
var winningid = $("#round_"+currentRound+" #selectable_"+i+" > li.selected").attr("id")
//its in the form team_
var id = winningid.split("_")[1]
var winningname = $("#round_"+currentRound+" #selectable_"+i+" > li.selected").attr("innerHTML")
winningTeams[winningTeams.length] = {id:id,name:winningname}
}
}
if(allGood) {
currentRound++
//first round
rounds[currentRound] = []
//assign new games
for(i=0;i < winningTeams.length; i=i+2) {
var game = {}
game.team1 = winningTeams[i]
if(winningTeams[i+1] != null) game.team2 = winningTeams[i+1]
rounds[currentRound][rounds[currentRound].length] = game
}
//draw the games
drawGames(currentRound)
}
}
First, we grab a pointer to the current round. I added a variable to my code to store the current round so this is globally available. Next I set a simple boolean value, allGood, that I'll use as a flag to see if the round is done. From the previous blog entry we know that a round is an array of games. I loop over each item in the array and get a count of the number of items with the selected class:
var totalselected = $("#round_"+currentRound+" #selectable_"+i+" > li.selected").size()
This was the first time I had used the size() attribute and, honestly, the first time I had need of it. As you can guess, it simply gives you a count of the number of items matched by your selector. Each round is a UL wrapped by a div named round_X where X is a the round number. So the selector says: Within round X, game i, count the LI tags with the selected class. If the matched size is not 1 we can set allGood to false and break out of the loop.
If the matched size is 1, notice I then grab the selected item, which is the winning team, and begin to parse data about it. I store the teams ID value (which if you remember from the first entry would have come from the database) and the teams name. This gets added to a new array called winningTeams.
The last thing we do is loop over the winningTeams array and add it as a new round. Once we've created new games from the winning teams, we can call drawGames() again. The end result is that we can now pick winners until we run out of teams to pick!
You can view a demo of this here. It's definitely moving along now but we have two final problems. First, if you do indeed get to the end you will notice a JavaScript error. The code simply can't handle running out of rounds. Second, once we do get to the end, we need to serialize the results. What teams you picked and at what round. Once serialized we could send it to the server for storage. There are a few other things we should look at as well. You can continue to click teams in earlier rounds, but doing so doesn't impact the later rounds. It would be easiest probably to just remove the toggle feature once you've completed a round. We should also look at improving the UI a bit so each round is a bit more distinct.
Thoughts so far?




Facebook Application Development
Comments
Leave a comment