-
Notifications
You must be signed in to change notification settings - Fork 8
/
ch02-interact-javascript.html
518 lines (412 loc) · 24.6 KB
/
ch02-interact-javascript.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
<section data-type="chapter" id="chapter02">
<h1>Interacting With JavaScript and Web Pages</h1>
<p>
Since ClojureScript compiles to JavaScript, you need to have a way to interact with native JavaScript and with web pages. In this chapter, you will find out five different ways to do this:
</p>
<ol>
<li>Direct use of JavaScript</li>
<li>The Google Closure library</li>
<li>The Dommy library</li>
<li>The Domina library</li>
<li>The Enfocus library</li>
</ol>
<div data-type="note">
All of these methods are fairly “old school.” As of this writing, all the Cool Kids<sup>™</sup> are using <em>reactive programming</em> to handle the user interface. I still think it is useful to have knowledge of the older methods, as they might sometimes be the right tool to solve a problem. However, if you want, see <a href="#chapter05" data-type="xref">#chapter05</a>.
</div>
<p>
You’ll be doing the same task with each of these: calculating the number of hours of daylight based on a latitude and Julian date, as in <a href="#CH01-ET05" data-type="xref">#CH01-ET05</a>.
Here is the relevant HTML:
</p>
<pre data-type="programlisting" data-code-language="html">
<!DOCTYPE html>
<html>
<head>
<title>Daylight Minutes</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<h1>Daylight Minutes</h1>
<p>
Latitude: <input type="text" size="8" id="latitude" />&#176;<br />
Day of year: <input type="text" size="4" id="julian" /><br />
<input type="button" value="Calculate" id="calculate"/>
</p>
<p>
Minutes of daylight: <span id="result"></span>
</p>
<script src="out/<em>project_name</em>.js" type="text/javascript"></script>
</body>
</html></pre>
<p>I suggest you create a new project for each of these études and copy the preceding HTML into the project’s <em>index.html</em> file. Remember to make the
<code>src</code> attribute of the <code>script</code> element match your project name.</p>
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<section data-type="sect1" id="CH02-ET01">
<h1>Étude 2-1: Direct use of JavaScript</h1>
<p>This is the most direct method to interact with a page, and is the least ClojureScript-like in its approach.</p>
<section data-type="sect2" id="javascript-methods">
<h2>Invoking Methods</h2>
<p>In order to invoke JavaScript methods directly, you use expressions of the general form:</p>
<pre data-type="programlisting" data-code-language="clojurescript">(.<em>methodname</em> <em>JavaScript object</em> <em>arguments</em>)</pre>
<p>Here are some examples you can try in the REPL.</p>
<pre data-type="programlisting" data-code-language="clojurescript">;; call the sqrt function from JavaScript's Math object with an argument 3
(.sqrt js/Math 3)
;; equivalent of window.parseFloat("3.5")
(.parseFloat js/window "3.5")
;; equivalent of "shouting".toUpperCase()
(.toUpperCase "shouting")
;; equivalent of "ClojureScript".substr(2,3)
(.substr "ClojureScript" 2 3)
;; equivalent of document.getElementById("latitude")
(.getElementById js/document "latitude")</pre>
<p>
You can also use a different form for methods that belong to the <code>js</code> namespace:
</p>
<pre data-type="programlisting" data-code-language="clojurescript">;; call the sqrt function from JavaScript's Math object with an argument 3
(js/Math.sqrt 3)
;; equivalent of window.parseFloat("3.5")
(js/Window.parseFloat "3.5")
;; equivalent of document.getElementById("latitude")
(js/document.getElementById "latitude")</pre>
</section>
<section data-type="sect2" id="javascript-properties">
<h2>Accessing Properties</h2>
<p>
To access an object’s properties, use <code>.-</code>; before you try these in the browser REPL, type something into the <code>latitude</code> field in the form.
</p>
<pre data-type="programlisting" data-code-language="clojurescript">
;; equivalent of Math.PI
(.-PI js/Math)
;; equivalent of "ClojureScript".length
(.-length "ClojureScript")
;; equivalent of document.getElementById("latitude").value
(.-value (.getElementById js/document "latitude"))
;; setting properties: equivalent of document.getElementById("latitude").value = 23.5;
(set! (.-value (.getElementById js/document "latitude")) 23.5)</pre>
</section>
<section data-type="sect2" id="javascript-objects">
<h2>Creating JavaScript Objects</h2>
<p>
This étude doesn’t need you to create any JavaScript objects, but if you are interacting with an existing library you may need to do so. To create an object, give the class name followed by a period:
</p>
<pre data-type="programlisting" data-code-language="clojurescript">;; equivalent of d = new Date
(def d (js/Date.))
;; now you can use it
(.getHours d)
;; if you need a true JavaScript Array object
(def arr (js/Array. 10 20 30))
(get arr 2)</pre>
</section>
<section data-type="sect2" id="javascript-events">
<h2>Listening for Events</h2>
<p>
In JavaScript, if you want an HTML element to respond to an event, you add an event listener to that element, tell it what type of event you want to listen for, and give it the name of a function that handles the event. That event handling function must have one parameter to hold the event object. In ClojureScript, you need to define functions before you use them, so you have to write the event handler first and then invoke <code>addEventListener</code>. Here is an example of what I did in the REPL (my project name was <code>daylight_js</code>.
</p>
<pre data-type="programlisting">cljs.user=> (in-ns 'daylight_js.core)
nil
daylight_js.core=> (defn testing [evt] (.alert js/window "You clicked me!!!"))
#<function daylight_js$core$testing(evt){
return window.alert("You clicked me!!!");
}>
daylight_js.core=> (let [btn (.getElementById js/document "calculate")]
(.addEventListener btn "click" testing))
nil</pre>
<p>
The first line switches to the correct namespace for the project. The second line defines the event handler, which calls JavaScript’s <code>alert()</code> function to display a message. The third line tells the “Calculate” button to listen for <code>click</code> events and call the <code>testing</code> function when they occur.
</p>
</section>
<p>Given this information, complete the code for the project such that, when you click the “Calculate” button, the program will read the values from the latitude and
Julian day field, calculate the number of daylight hours, and place the result in the <code><span id="result"></code>. (Hint: use the <code>innerHTML</code> property.) You may also want to write a function that takes a form field name as its argument and returns the floating-point value from that field.</p>
<p>See a suggested solution: <a href="#SOLUTION02-ET01" data-type="xref">#SOLUTION02-ET01</a></p>
</section>
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<section data-type="sect1" id="CH02-ET02">
<h1>Étude 2-2: Using Google Closure</h1>
<p>Using JavaScript directly is all well and good; one advantage is that (if you’re a JavaScript programmer), you already know this stuff. The bad news is that you have all the problems of getting JavaScript to work on multiple browsers and platforms. Enter <a href="https://developers.google.com/closure/">Google Closure</a>, a library of JavaScript utilities that has all of those nasty compatibility parts all figured out for you. In this étude, you’ll use Closure for the interaction.</p>
<section data-type="sect2" id="closure-require">
<h2>Putting Google Closure into Your Project</h2>
<p>
To use Google Closure, you need to change the first lines of your <em>core.cljs</em> file to require the code that maniuplates the DOM and handles events. In this example, the project has been named <code>daylight_gc</code>.
</p>
<pre data-type="programlisting" data-code-language="clojurescript">(ns daylight_gc.core
(:require [clojure.browser.repl :as repl]
[goog.dom :as dom]
[goog.events :as events]))</pre>
<p>
In the REPL, type <code>(require 'goog.dom :as dom)</code> to access the code.
</p>
</section>
<section data-type="sect2" id="closure-dom">
<h2>Using Google Closure to Access the DOM</h2>
<p>
When accessing DOM elements, the main difference between Closure and pure JavaScript is that you use <code>dom/getElement</code> instead of <code>.getElementById js/document</code>. Thus, after starting the browser REPL and typing 55 into the latitude input area:
</p>
<pre>cljs.user=> (require 'daylight_gc.core)
nil
cljs.user=> (in-ns 'daylight_gc.core)
nil
daylight_gc.core=> (require '[goog.dom :as dom])
nil
daylight_gc.core=> (dom/getElement "latitude")
#<[object HTMLInputElement]>
daylight_gc.core=> (.-value (dom/getElement "latitude"))
"55"
daylight_gc.core=> (set! (.-value (dom/getElement "latitude")) -20)
-20
daylight_gc.core=> ;; Closure has its own way to set an element's text
daylight_gc.core=> (dom/setTextContent (dom/getElement "result") "Here is some text")
nil
</pre>
</section>
<section data-type="sect2" id="closure-events">
<h2>Using Google Closure to Handle Events</h2>
<p>
Again, the code is quite similar to what you would do with plain JavaScript; you use <code>events/listener</code> instead of <code>.addListener</code>. The following adds a listener to the “Calculate” button.
</p>
<pre>daylight_gc.core=> (defn testing [evt] (.alert js/window "Clickety-click"))
#<function daylight_gc$core$testing(evt){
return window.alert("Clickety-click");
}>
daylight_gc.core=> (events/listen (dom/getElement "calculate") "click" testing)
#<[object Object]></pre>
<p>
After you test it, you may want to remove the listener so that it doesn’t interfere with the code you put in your source <em>core.cljs</em> file.
</p>
<pre>daylight_gc.core=> (events/unlisten (dom/getElement "calculate") "click" testing)
true</pre>
</section>
<p>Given this information, complete the code for the project. Note: If you created a new project and just copy/pasted the <em>index.html</em> file, make sure you change the <code><script></code> element to refer to the right file.</p>
<p>See a suggested solution: <a href="#SOLUTION02-ET02" data-type="xref">#SOLUTION02-ET02</a></p>
</section>
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- DOMMY -->
<section data-type="sect1" id="CH02-ET03">
<h1>Étude 2-3: Using dommy</h1>
<p>While Google Closure gives you a lot of great code, it’s still JavaScript, and it “feels” like JavaScript. What you would like is a library that gives you the capabilities but in a more functional way. One of those libraries is <a href="https://github.com/Prismatic/dommy">dommy</a>. In this étude, you will use dommy to interact with the web page.</p>
<section data-type="sect2" id="dommy-require">
<h2>Putting dommy into Your Project</h2>
<p>
To use dommy, you need to change the first lines of your <em>core.cljs</em> file to require the code that maniuplates the DOM and handles events. In this example, the project has been named <code>daylight_dommy</code>.
</p>
<pre data-type="programlisting" data-code-language="clojurescript">(ns daylight_dommy.core
(:require [clojure.browser.repl :as repl]
[dommy.core :as dommy :refer-macros [sel sel1]]))</pre>
<p>
The <code>:refer-macros</code> is new, and beyond the scope of this book. The oversimplified explanation is that ClojureScript macros are like functions with extra super powers. I <em>will</em> explain the <code>sel</code> and <code>sel1</code> later.
</p>
<p>
You also need to change the <em>project.clj</em> file to specify dommy as one of your project’s dependencies. The additional code is highlighted:
</p>
<pre> :dependencies [[org.clojure/clojure "1.7.0-beta2"]
[org.clojure/clojurescript "0.0-3211"]
<strong>[prismatic/dommy "1.1.0"]</strong>]</pre>
</section>
<section data-type="sect2" id="dommy-dom">
<h2>Using dommy to Access the DOM</h2>
<p>
Dommy has two functions for accessing elements: <code>sel1</code> and <code>sel</code>. <code>sel1</code> will return a single HTML node; <code>sel</code> will return a JavaScript array of all matching elements. The <em>index.html</em> file has three <code><input/></code> elements. Compare the results:
</p>
<pre>cljs.user=> ;; set up name spaces
cljs.user=> (require 'daylight_dommy.core)
nil
cljs.user=> (in-ns 'daylight_dommy.core)
nil
daylight_dommy.core=> (require '[dommy.core :as dommy :refer-macros [sel sel1]])
nil
daylight_dommy.core=> ;; access the first <input> element
daylight_dommy.core=> (sel1 "input")
#<[object HTMLInputElement]>
daylight_dommy.core=> ;; access all the <input> elements
daylight_dommy.core=> (sel "input")
#js [#<[object HTMLInputElement]> #<[object HTMLInputElement]> #<[object HTMLInputElement]>]
daylight_dommy.core=> ;; since IDs are unique, you use sel1 for them.
daylight_dommy.core=> (sel1 "#latitude")
#<[object HTMLInputElement]></pre>
<p>
To access values of form fields, use dommy’s <code>value</code> and <code>set-value!</code> functions. (I typed 55 into the latitude field before doing these commands.) Similarly, <code>text</code> and <code>set-text!</code> let you read and write text content of elements. <code>html</code> and <code>set-html!</code> let you read and write HTML content of an element. Notice that you can use either a string or a keyword as an argument to <code>sel</code>.
</p>
<pre>daylight_dommy.core=> ;; retrieve and set form field
daylight_dommy.core=> (dommy/value (sel1 "#latitude"))
"55"
daylight_dommy.core=> (dommy/set-value! (sel1 "#latitude") 10.24)
#<[object HTMLInputElement]>
daylight_dommy.core=> ;; set and retrieve text content
daylight_dommy.core=> (dommy/set-text! (sel1 :#result) "some text")
#<[object HTMLSpanElement]>
daylight_dommy.core=> (dommy/text (sel1 :#result))
"some text"
daylight_dommy.core-> (dommy/set-html! (sel1 :#result) "<i>Yes!</i>")</pre>
</section>
<section data-type="sect2" id="dommy-events">
<h2>Using dommy to Handle Events</h2>
<p>
Here is the code to add and remove an event listener. You may use either keywords or strings for event names. If you use a keyword for the event name, such as <code>:click</code> when you listen for events, you <em>must</em> use a keyword when you remove the listener.
</p>
<pre>daylight_dommy.core=> (defn testing [event] (.alert js/window "Clicked."))
#<function daylight_dommy$core$testing(event){
return window.alert("Clicked.");
}>
daylight_dommy.core=> (dommy/listen! (sel1 :#calculate) :click testing)
#<[object HTMLInputElement]>
daylight_dommy.core=> ;; the web page should now respond to clicks. Try it.
daylight_dommy.core=> ;; now remove the listener.
daylight_dommy.core=> (dommy/unlisten! (sel1 "#calculate") :click testing)
#<[object HTMLInputElement]>
daylight_dommy.core=>
</pre>
</section>
<p>Given this information, complete the code for the project. Note: If you created a new project and just copy/pasted the <em>index.html</em> file, make sure you change the <code><script></code> element to refer to the right file.</p>
<p>See a suggested solution: <a href="#SOLUTION02-ET03" data-type="xref">#SOLUTION02-ET03</a></p>
</section>
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- DOMINA -->
<section data-type="sect1" id="CH02-ET04">
<h1>Étude 2-4: Using Domina</h1>
<p>The <a href="https://github.com/levand/domina">Domina</a> library is very similar in approach to dommy. In this étude, you will use Domina to interact with the web page.</p>
<section data-type="sect2" id="domina-require">
<h2>Putting Domina into Your Project</h2>
<p>
To use Domina, you need to change the first lines of your <em>core.cljs</em> file to require the code that maniuplates the DOM and handles events. In this example, the project has been named <code>daylight_domina</code>.
</p>
<pre data-type="programlisting" data-code-language="clojurescript">(ns daylight_dommy.core
(:require [clojure.browser.repl :as repl]
[domina]
[domina.events :as events]))</pre>
<p>
The <code>:refer-macros</code> is new, and beyond the scope of this book. The oversimplified explanation is that ClojureScript macros are like functions with extra super powers. I <em>will</em> explain the <code>sel</code> and <code>sel1</code> later.
</p>
<p>
You also need to change the <em>project.clj</em> file to specify dommy as one of your project’s dependencies. The additional code is highlighted:
</p>
<pre> :dependencies [[org.clojure/clojure "1.7.0-beta2"]
[org.clojure/clojurescript "0.0-3211"]
<strong>[domina "1.0.3"]</strong>]</pre>
</section>
<section data-type="sect2" id="domina-dom">
<h2>Using Domina to Access the DOM</h2>
<p>
In Domina, you can access an item by its ID, by a CSS class, or by an XPath expression. This étude only uses the first of these methods with the <code>by-id</code> function.
</p>
<pre>cljs.user=> ;; set up name spaces
cljs.user=> (require 'daylight_domina.core)
nil
cljs.user=> (in-ns 'daylight_domina.core)
nil
daylight_domina.core=> (require 'domina)
nil
daylight_domina.core=> (require '[domina.events :as events])
nil
daylight_domina.core=> (domina/by-id "latitude")
#<[object HTMLInputElement]></pre>
<p>
To access values of form fields, use Domina’s <code>value</code> and <code>set-value!</code> functions. (I typed 55 into the latitude field before doing these commands.) Similarly, <code>text</code> and <code>set-text!</code> let you read and write text content of elements. <code>html</code> and <code>set-html!</code> let you read and write HTML content of an element. Notice that you can use either a string or a keyword as an argument to <code>sel</code>.
</p>
<pre>daylight_domina.core=> ;; retrieve and set form field
daylight_domina.core=> (domina/value (domina/by-id "#latitude"))
"55"
daylight_domina.core=> (domina/set-value! (domina/by-id "#latitude") 10.24)
#<[object HTMLInputElement]>
daylight_domina.core=> ;; set and retrieve text content
daylight_domina.core=> (domina/set-text! (domina/by-id :result) "Testing 1 2 3")
#<[object HTMLSpanElement]>
daylight_domina.core=> (def resultspan (domina/by-id :result)) ;; to save typing
#<[object HTMLSpanElement]>
daylight_domina.core=> (domina/text resultspan)
"Testing 1 2 3"
daylight_domina.core-> (domina/set-html! resultspan "<i>Yes!</i>")#
<[object HTMLSpanElement]>
daylight_domina.core=> ;; look at web page to see result</pre>
</section>
<section data-type="sect2" id="domina-events">
<h2>Using Domina to Handle Events</h2>
<p>
Here is the code to add and remove an event listener. You may use either keywords or strings for event names. You may use either a string or keyword when you remove the listener. The <code>unlisten!</code> function removes <em>all</em> listeners associated with the event type.
</p>
<pre>daylight_domina.core=> (defn testing [event] (.alert js/window "You clicked me."))
#<function daylight_domina$core$testing(event){
return window.alert("You clicked me.");
}>
daylight_domina.core=> (events/listen! (domina/by-id "calculate") :click testing)
#<[object HTMLInputElement]>
daylight_domina.core=> ;; the web page should now respond to clicks. Try it.
daylight_domina.core=> ;; now remove the listener.
daylight_domina.core=> (events/unlisten! (domina/by-id "calculate") "click")
#<[object HTMLInputElement]>
daylight_domina.core=> </pre>
</section>
<p>Given this information, complete the code for the project. Note: If you created a new project and just copy/pasted the <em>index.html</em> file, make sure you change the <code><script></code> element to refer to the right file.</p>
<p>See a suggested solution: <a href="#SOLUTION02-ET04" data-type="xref">#SOLUTION02-ET04</a></p>
</section>
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- ENFOCUS -->
<section data-type="sect1" id="CH02-ET05">
<h1>Étude 2-5: Using Enfocus</h1>
<p>The <a href="https://github.com/ckirkendall/enfocus">Enfocus</a> library is very different from dommy and Domina.</p>
<section data-type="sect2" id="enfocus-require">
<h2>Putting Enfocus into Your Project</h2>
<p>
To use Enfocus, you need to change the first lines of your <em>core.cljs</em> file to require the code that maniuplates the DOM and handles events. In this example, the project has been named <code>daylight_enfocus</code>.
</p>
<pre data-type="programlisting" data-code-language="clojurescript">(ns daylight_dommy.core
(:require [clojure.browser.repl :as repl]
[enfocus.core :as ef]
[enfocus.events :as ev]))</pre>
<p>
You also need to change the <em>project.clj</em> file to specify Enfocus as one of your project’s dependencies. The additional code is highlighted:
</p>
<pre> :dependencies [[org.clojure/clojure "1.7.0-beta2"]
[org.clojure/clojurescript "0.0-3211"]
<strong>[enfocus "2.1.0"]</strong>]</pre>
</section>
<section data-type="sect2" id="enfocus-dom">
<h2>Using Enfocus to Access the DOM</h2>
<p>
The idea behind Enfocus is that you select a node and then do transformations on it. This is a very powerful concept, but this étude will use only its simplest forms. First, set up namespaces:
</p>
<pre>cljs.user=> (require 'daylight_enfocus.core)
nil
cljs.user=> (in-ns 'daylight_enfocus.core)
nil
daylight_enfocus.core=> (require '[enfocus.core :as ef])
nil
daylight_enfocus.core=> (require '[enfocus.events :as ev])
nil</pre>
<p>
Enfocus lets you select an element by its ID either as a CSS selector, an <a href="https://github.com/cgrand/enlive">Enlive</a> selector, or an XPath Selector. In this case, let’s just stick with the old familar CSS form. To access values of form fields, use Enfocus’s <code>from</code> function to select the field, then use the <code>get-prop</code> transformation to extract the value. (I typed 55 into the latitude field before doing these commands.) Similarly, <code>at</code> selects an element you want to alter, and the <code>content</code> and <code>html-content</code> transformation lets you set an element’s content.
</p>
<pre>daylight_enfocus.core=> (ef/from "#latitude" (ef/get-prop :value))
"55"
daylight_enfocus.core=> (ef/at "#latitude" (ef/set-prop :value 10.24))
nil
daylight_enfocus.core=> (ef/at "#result" (ef/content "New text"))
nil
daylight_enfocus.core=> (ef/at "#result" (ef/html-content "<i>Improved text</i>"))
nil
daylight_enfocus.core=> ;; look at web page to see result</pre>
<p>
Note: when you use the <code>content</code> transformation, the argument must be a string or a node. You can’t use a number―you must convert it to a string:
</p>
<pre>daylight_enfocus.core=> (ef/at "#result" (ef/content (.toString 3.14159)))
nil</pre>
</section>
<section data-type="sect2" id="enfocus-events">
<h2>Using Enfocus to Handle Events</h2>
<p>
Here is the code to add and remove an event listener.
</p>
<pre>daylight_enfocus.core=> (defn testing [evt] (.alert js/window "Click-o-rama"))
#<function daylight_enfocus$core$testing(evt){
return window.alert("Click-o-rama");
}>
daylight_enfocus.core=> (ef/at "#calculate" (ev/listen :click testing))
nil
daylight_enfocus.core=> ;; the web page should now respond to clicks. Try it.
daylight_enfocus.core=> ;; now remove the listener.
daylight_enfocus.core=> (ef/at "#calculate" (ev/remove-listeners :click))
nil</pre>
</section>
<p>
Given this information, complete the code for the project. Note: If you created a new project and just copy/pasted the <em>index.html</em> file, make sure you change the <code><script></code> element to refer to the right file.
</p>
<p>See a suggested solution: <a href="#SOLUTION02-ET05" data-type="xref">#SOLUTION02-ET05</a></p>
</section>
</section>