Every year my in-laws do the whole “drawing names” thing at Christmas, where each person draws one other person and buys a gift for them. Nearly as often, there’s drama about it. Someone complains about “always” getting someone else. Someone gets themself (that was me this year).
I decided to remove the human element from it and make a little tool that would do the draw for us. As my wife said, “Just because you write an app doesn’t mean things are gonna change.” It was a good little exercise, though.
To make it more of an exercise, I did the entire thing on the client-side. I do enough server-side work that it would have been really easy to fall back on that and not learn anything. It’s a little sloppy but it works.
On to the code…
I start with a div that contains a header and an empty list. This div is hidden on page load and will contain the list of names as they’re added. Then I’ve got a form for adding names to the list, clearing the list, generating the drawn list, and for determining whether or not we’re going to allow people to draw themselves. That’s it for the page content, everything else is JavaScript/jQuery.
I start out by giving myself a global array called names to hold the drawn names. As previously mentioned, on page load I hide the div containing the names list. On submit of the form I run a function called add_name().
To clear the name list I wipe out the contents of that particular UL and re-hide the div containing it. Then I make sure to disable the buttons for generating a list and clearing the list (since there’s no list to work with) and enable the ability to add names.
To generate the list I have a populate_list() function wrapped in button enabling/disabling. All of the buttons are disabled, then populate_list() runs, then we re-enable the Generate and Clear List buttons.
Okay, some actual functionality. The add_name() function is what actually puts an entered name into the list. First we get the value of the new_name input field and trim it. If there’s actually something there, we show the name list container (in case it wasn’t already visible) and add this new name to the list as a list item, with some extra spans for structure. The name itself is in a span with the giver class while there are empty spans with classes of arrow and recipient.
Then I check the length of the name list. If there are any names (as there should be since we just added one), the ability to clear the list becomes available. If there’s more than one, the ability to generate a list from them is open. Then I clear out the contents of the new_name input field.
Now we’ve got the actual meat of the list generation, the populate_list() function. We start by blanking out the array of names (just in case), then we loop through the contents of the name list UL to re-populate it. Stripping out the span surrounding the name itself should work by doing $($(this).html()).text() but it didn’t for me so I brute forced it and just replaced the span tags with nothing. Then we shuffle that list with a function I grabbed from StackOverflow.
I loop through the UL a second time to assign a recipient to each giver. I strip out the offending HTML again (I could have pulled that into a function but didn’t out of laziness).
If the only name in the names array is the same as the one we’re working on and we’re not allowing names to select themselves, we’ve hit a problem. In that case, we throw the whole thing out and brute-force it, re-running the populate_list() function.
If that’s not the case, I shuffle the names array until either the first name in the array is not the one we’re currently drawing for or people are allowed to draw themselves. For aesthetics I pop an arrow in the arrow span for the LI we’re working on, then I put the first name from the names array in the recipient span. I wrap it up by shifting the array to get that name out of the way.
There’s more brute-force than I’d like, defaulting to shuffling whenever we find a conflict, but it seems like a real-world name list would be around ten people and I don’t see an issue with performance at that level. No, it’s not the best code but it does what it needs to do.