[#]: collector: (lujun9972) [#]: translator: ( ) [#]: reviewer: ( ) [#]: publisher: ( ) [#]: url: ( ) [#]: subject: (How I programmed a virtual gift exchange) [#]: via: (https://opensource.com/article/21/1/open-source-gift-exchange) [#]: author: (Chris Hermansen https://opensource.com/users/clhermansen) How I programmed a virtual gift exchange ====== A book club takes its annual gift exchange online with the help of HTML, CSS, and JavaScript. ![Package wrapped with brown paper and red bow][1] Every year, my wife's book club has a book exchange during the holidays. Due to the need to maintain physical distance in 2020, I created an online gift exchange for them to use during a book club videoconference. Apparently, the virtual book exchange worked out (at least, I received kind compliments from the book club members), so I decided to share this simple little hack. ### How the book exchange usually works In past years, the exchange has gone something like this: 1. Each person buys a book and wraps it up. 2. Everyone arrives at the host's home and puts the wrapped books in a pile. 3. Each person draws a number out of a hat, which establishes their turn. 4. The person who drew No. 1 selects a book from the pile and unwraps it. In turn, each subsequent person chooses to either take a wrapped book from the pile or to steal an unwrapped book from someone who has gone before. 5. When someone's book is stolen, they can either replace it with a wrapped book from the pile or steal another book (but not the one that was stolen from them) from someone else. 6. And so on… eventually, someone has to take the last unwrapped book to end the exchange. ### Designing the virtual book exchange My first decision was which implementation platform to use for the book exchange. Because there would already be a browser open to host the videoconference, I decided to use HTML, CSS, and JavaScript. Then it was design time. After some thinking, I decided to use rectangles to represent the book club members and the books. The books would be draggable, and when one was dropped on a member's rectangle, the book would unwrap (and stay unwrapped). I needed some "wrapping paper," so I used this source of [free-to-use images][2]. I took screenshots of the patterns I liked and used [GIMP][3] to scale the images to the right width and height. I needed a way to handle draggable and droppable interactions; given that I've been using jQuery and jQuery UI for several years now, I decided to continue along that path. For a while, I struggled with what a droppable element should do when something was dropped on it. Finally, I realized that all I needed to do was unwrap the dropped item (if it was still wrapped). I also spent some time fretting over how to lay stuff out until I realized that the easiest thing to do was just leave the elements floating. Jumping to the results, here's a screenshot of the user interface at the beginning of the exchange: ![Virtual book exchange][4] (Chris Hermansen, [CC BY-SA 4.0][5]) There are nine book club members: Wanda, Carlos, Bill, and so on. There are also nine fairly ugly wrapped parcels. Let's say Wanda goes first and chooses the flower wrapping paper. The host clicks and drags that parcel to Wanda's name, and the parcel unwraps: ![Virtual book exchange][6] (Chris Hermansen, [CC BY-SA 4.0][5]) Whoops! That title and author are a bit too long to fit on the book's "cover." Oh well, I'll fix that in the next version. Carlos is next. He decides he really wants to read that book, so he steals it. Wanda then chooses the paisley pattern, and the screen looks like this: ![Virtual book exchange][7] (Chris Hermansen, [CC BY-SA 4.0][5]) And so on until the exchange ends. ### The code So what about the code? Here it is: ``` 1 <!doctype html> 2 <[html][8] lang="en"> 3 <[head][9]> 4 <[meta][10] charset="utf-8"> 5 <[title][11]>Book Exchange</[title][11]> 6 <[link][12] rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css"> 7 <[style][13]> 8 .draggable { 9 float: left; 10 width: 90px; 11 height: 90px; 12 background: #ccc; 13 padding: 5px; 14 margin: 5px 5px 5px 0; 15 } 16 .droppable { 17 float: left; 18 width: 100px; 19 height: 125px; 20 background: #999; 21 color: #fff; 22 padding: 10px; 23 margin: 10px 10px 10px 0; 24 } 25 </[style][13]> 26 <[script][14] src="[https://code.jquery.com/jquery-1.12.4.js"\>\][15]</[script][14]> 27 <[script][14] src="[https://code.jquery.com/ui/1.12.1/jquery-ui.js"\>\][16]</[script][14]> 28 </[head][9]> 29 <[body][17]> 30 <[h1][18] style="color:#1a1aff;">Raffles Book Club Remote Gift Exchange</[h1][18]> 31 <[h2][19] style="color:#aa0a0a;">The players, in random order, and the luxurious gifts, wrapped:</[h2][19]> 32 33 <[div][20]> 34 <[div][20] id="wanda" class="droppable">Wanda</[div][20]> 35 <[div][20] id="carlos" class="droppable">Carlos</[div][20]> 36 <[div][20] id="bill" class="droppable">Bill</[div][20]> 37 <[div][20] id="arlette" class="droppable">Arlette</[div][20]> 38 <[div][20] id="joanne" class="droppable">Joanne</[div][20]> 39 <[div][20] id="aleks" class="droppable">Alekx</[div][20]> 40 <[div][20] id="ermintrude" class="droppable">Ermintrude</[div][20]> 41 <[div][20] id="walter" class="droppable">Walter</[div][20]> 42 <[div][20] id="hilary" class="droppable">Hilary</[div][20]> 43 </[div][20]> 44 <[div][20]> 45 <[div][20] id="bows" class="draggable" style="background-image: url('bows.png');"></[div][20]> 46 <[div][20] id="boxes" class="draggable" style="background-image: url('boxes.png');"></[div][20]> 47 <[div][20] id="circles" class="draggable" style="background-image: url('circles.png');"></[div][20]> 48 <[div][20] id="gerbers" class="draggable" style="background-image: url('gerbers.png');"></[div][20]> 49 <[div][20] id="hippie" class="draggable" style="background-image: url('hippie.png');"></[div][20]> 50 <[div][20] id="lattice" class="draggable" style="background-image: url('lattice.png');"></[div][20]> 51 <[div][20] id="nautical" class="draggable" style="background-image: url('nautical.png');"></[div][20]> 52 <[div][20] id="splodges" class="draggable" style="background-image: url('splodges.png');"></[div][20]> 53 <[div][20] id="ugly" class="draggable" style="background-image: url('ugly.png');"></[div][20]> 54 </[div][20]> 55 56 <[script][14]> 57 var books = { 58 'bows': 'Untamed by Glennon Doyle', 59 'boxes': "The Heart's Invisible Furies by John Boyne", 60 'circles': 'The Great Halifax Explosion by John Bacon', 61 'gerbers': 'Homes: A Refugee Story by Abu Bakr al Rabeeah, Winnie Yeung', 62 'hippie': 'Before We Were Yours by Lisa Wingate', 63 'lattice': "Hamnet and Judith by Maggie O'Farrell", 64 'nautical': 'Shuggy Bain by Douglas Stewart', 65 'splodges': 'Magdalena by Wade Davis', 66 'ugly': 'Funny Boy by Shyam Selvadurai' 67 }; 68 $( ".droppable" ).droppable({ 69 drop: function(event, ui) { 70 var element = $(ui.draggable[0]); 71 var wrapping = element.attr('id'); 72 /* alert( $(this).text() + " got " + wrapping); */ 73 $(ui.draggable[0]).css("background-image","url(book_cover.png)"); 74 $(ui.draggable[0]).text(books[wrapping]); 75 }, 76 out: function() { 77 /* alert( $(this).text() + " lost it" ); */ 78 } 79 }); 80 $( ".draggable" ).draggable(); 81 </[script][14]> 82 83 </[body][17]> 84 </[html][8]> ``` ### Breaking it down Let's go over this code bit by bit. * **Lines 1–6:** Upfront, I have the usual HTML boilerplate, `HTML`, `HEAD`, `META`, `TITLE` elements, followed by a link to the CSS for jQuery UI. * **Lines 7–25:** I added two new style classes: `draggable` and `droppable`. These define the layout for the books (draggable) and the people (droppable). Note that, aside from defining the size, background color, padding, and margin, I established that these need to float left. This way, the layout adjusts to the browser window width in a reasonably acceptable form. * **Line 26–27:** With the CSS out of the way, it's time for the JavaScript libraries, first jQuery, then jQuery UI. * **Lines 29–83:** Now that the `HEAD` element is done, next is the `BODY`: * **Lines 30–31:** These couple of titles, `H1` and `H2`, let people know what they're doing here. * **Lines 33–43:** A `DIV` to contain the people: * **Lines 34–42:** The people are defined as droppable `DIV` elements and given `ID` fields corresponding to their names. * **Lines 44–54:** A `DIV` to contain the books: * **Lines 45–53:** The books are defined as draggable `DIV` elements. Each element is declared with a background image corresponding to the wrapping paper with no text between the `