1+ <!DOCTYPE html>
2+ < html lang ="en ">
3+ < head >
4+ < meta charset ="UTF-8 " />
5+ < meta name ="viewport " content ="width=device-width, initial-scale=1.0 " />
6+ < title > beJUG Raffle</ title >
7+ < style >
8+ body {
9+ margin : 0 ;
10+ font-family : Arial, sans-serif;
11+ background-color : # 121212 ;
12+ color : # ffffff ;
13+ display : flex;
14+ flex-direction : column;
15+ align-items : center;
16+ padding : 2rem ;
17+ }
18+ img {
19+ max-width : 200px ;
20+ margin-bottom : 1rem ;
21+ }
22+ input {
23+ width : 100% ;
24+ max-width : 500px ;
25+ padding : 0.5rem ;
26+ margin-bottom : 1rem ;
27+ background-color : # 1e1e1e ;
28+ color : # ffffff ;
29+ border : 1px solid # 333 ;
30+ }
31+ button {
32+ background-color : # ff4081 ;
33+ color : white;
34+ border : none;
35+ padding : 0.75rem 1.5rem ;
36+ cursor : pointer;
37+ font-size : 1rem ;
38+ border-radius : 5px ;
39+ margin : 0.5rem ;
40+ }
41+ button : hover {
42+ background-color : # e91e63 ;
43+ }
44+ button : disabled {
45+ background-color : # 777 ;
46+ cursor : not-allowed;
47+ }
48+ # participantsList {
49+ display : flex;
50+ flex-wrap : wrap;
51+ gap : 0.5rem ;
52+ max-width : 500px ;
53+ width : 100% ;
54+ margin-bottom : 1rem ;
55+ }
56+ .tag {
57+ background-color : # 333 ;
58+ padding : 0.4rem 0.75rem ;
59+ border-radius : 20px ;
60+ font-size : 0.9rem ;
61+ display : flex;
62+ align-items : center;
63+ }
64+ .tag span {
65+ margin-left : 0.5rem ;
66+ color : # ff4081 ;
67+ cursor : pointer;
68+ }
69+ .tag span : hover {
70+ color : # f44336 ;
71+ }
72+ # winner {
73+ margin-top : 1rem ;
74+ font-size : 1.5rem ;
75+ font-weight : bold;
76+ color : # 4caf50 ;
77+ }
78+ # animation {
79+ font-size : 2rem ;
80+ margin-top : 2rem ;
81+ min-height : 2rem ;
82+ color : # ffeb3b ;
83+ font-weight : bold;
84+ }
85+ # winnerImage {
86+ margin-top : 2rem ;
87+ display : none;
88+ min-width : 450px ;
89+ }
90+ </ style >
91+ </ head >
92+ < body >
93+ < img src ="logo.png " alt ="BeJUG Logo " />
94+ < h1 > BeJUG Raffle</ h1 >
95+ < input type ="text " id ="nameInput " placeholder ="Type a name and press Enter " aria-label ="Add participant name " />
96+ < div id ="participantsList " role ="list "> </ div >
97+ < div >
98+ < button id ="drawButton " onclick ="drawWinner() " disabled > Draw Winner</ button >
99+ </ div >
100+ < div id ="animation "> </ div >
101+ < div id ="winner "> </ div >
102+ < img id ="winnerImage " src ="duke-celebration.png " alt ="Winner Celebration Image " />
103+
104+ <!-- Confetti library -->
105+ < script src ="https://cdn.jsdelivr.net/npm/canvas-confetti@1.5.1/dist/confetti.browser.min.js "> </ script >
106+
107+ < script >
108+ const participants = [ ] ;
109+
110+ const nameInput = document . getElementById ( "nameInput" ) ;
111+ const participantsList = document . getElementById ( "participantsList" ) ;
112+ const drawButton = document . getElementById ( "drawButton" ) ;
113+ const winnerDiv = document . getElementById ( "winner" ) ;
114+ const animationDiv = document . getElementById ( "animation" ) ;
115+ const winnerImage = document . getElementById ( "winnerImage" ) ;
116+
117+ nameInput . addEventListener ( "keypress" , function ( e ) {
118+ if ( e . key === "Enter" ) {
119+ e . preventDefault ( ) ;
120+ const name = e . target . value . trim ( ) ;
121+ if ( name && ! participants . includes ( name ) ) {
122+ participants . push ( name ) ;
123+ updateParticipantList ( ) ;
124+ }
125+ e . target . value = "" ;
126+ }
127+ } ) ;
128+
129+ function updateParticipantList ( ) {
130+ participantsList . innerHTML = "" ;
131+
132+ participants . forEach ( name => {
133+ const tag = document . createElement ( "div" ) ;
134+ tag . className = "tag" ;
135+ tag . setAttribute ( "role" , "listitem" ) ;
136+ tag . textContent = name ;
137+
138+ const x = document . createElement ( "span" ) ;
139+ x . textContent = "×" ;
140+ x . setAttribute ( "title" , "Remove " + name ) ;
141+ x . addEventListener ( "click" , ( ) => {
142+ const index = participants . indexOf ( name ) ;
143+ if ( index > - 1 ) {
144+ participants . splice ( index , 1 ) ;
145+ updateParticipantList ( ) ;
146+ }
147+ } ) ;
148+
149+ tag . appendChild ( x ) ;
150+ participantsList . appendChild ( tag ) ;
151+ } ) ;
152+
153+ drawButton . disabled = participants . length === 0 ;
154+ }
155+
156+ function getRandomParticipant ( ) {
157+ return participants [ Math . floor ( Math . random ( ) * participants . length ) ] ;
158+ }
159+
160+ function drawWinner ( ) {
161+ if ( participants . length === 0 ) return ;
162+
163+ winnerDiv . textContent = "" ;
164+ animationDiv . textContent = "Spinning..." ;
165+ winnerImage . style . display = "none" ;
166+
167+ let i = 0 ;
168+ const spinCount = 20 ;
169+
170+ const interval = setInterval ( ( ) => {
171+ animationDiv . textContent = getRandomParticipant ( ) ;
172+ i ++ ;
173+ if ( i >= spinCount ) {
174+ clearInterval ( interval ) ;
175+ const winner = getRandomParticipant ( ) ;
176+ animationDiv . textContent = "" ;
177+ winnerDiv . textContent = `🎉 Winner: ${ winner } 🎉` ;
178+ winnerImage . style . display = "block" ;
179+
180+ confetti ( {
181+ particleCount : 150 ,
182+ spread : 100 ,
183+ origin : { y : 0.6 }
184+ } ) ;
185+ }
186+ } , 100 ) ;
187+ }
188+ </ script >
189+ </ body >
190+ </ html >
0 commit comments