You cannot create the markup in index.html for the checklist items, because they need to be added after the page has already been rendered, in response to form submissions. Instead, you will add a Row constructor to the CheckList module.
The Row constructor will be in charge of
creating all the DOM elements necessary to represent
a single coffee order, including the checkbox and text description.
But the Row constructor will not be exported to the App
namespace. It will only be used internally by one of the CheckList.prototype
methods.
Add the Row constructor in checklist.js, just before the
App.CheckList = CheckList;
statement. It should accept an
argument called coffeeOrder
that will be the
same data that is sent to Truck.prototype.createOrder.
... this.$element = $(selector); if (this.$element.length === 0) { throw new Error('Could not find element with selector: ' + selector); } } function Row(coffeeOrder) { // Constructor code will go here } App.CheckList = CheckList; window.App = App; })(window);
Your Row constructor will use jQuery to build
DOM elements. You will declare variables for the individual
elements that make up a checklist item.
Then the constructor will append them together into a subtree of
DOM elements, as shown in Figure 11.4.
The CheckList will
take that subtree and attach it to the page’s DOM tree
as a child of the [data-coffee-order="checklist"]
element.
(The “[39x]” in the order description represents the caffeine strength.)
The DOM subtree created by the Row constructor in Figure 11.4 is the equivalent of the following markup:
<div data-coffee-order="checkbox" class="checkbox"> <label> <input type="checkbox" value="[email protected]"> tall mocha iced coffee, ([email protected]) [39x] </label> </div>
A <div>
with a
checkbox
class is used to house
your <label>
and an <input>
element. The checkbox
class will
apply the appropriate Bootstrap styles to the <div>
. The
data-coffee-order
attribute will be used in
your JavaScript when you need to trigger the click action on the
checkbox.
Note that the type
attribute of your
<input>
is also
checkbox
. This tells the browser
to draw the input as a checkbox form element. A plain text description of the
order comes right after the <input>
. The
<label>
wraps both the checkbox input and
the plain text description. This turns the text and the input
into a click target for the checkbox.
You will create the <label>
,
<div>
, and <input>
elements
one at a time. Then you will manually place the elements
inside of one another to create a DOM subtree that
you will attach to the live DOM (the DOM tree currently
shown on the page). You will also create a string
that holds the text description of the order, like “tall
mocha iced coffee, ([email protected]) [39x].”
To create these elements, you will use jQuery’s $ function. Up to now, you only used the $ function to select elements from the DOM, but it can also be used to create them.
First, you are going to create the <div>
by calling
the $ function in the Row constructor
in checklist.js.
Pass it two arguments describing the DOM element you want it to create.
Make the first argument a string with the HTML tag of
the DOM element, in this case '<div></div>'
.
Make the second argument an object that specifies the
attributes that jQuery should add to the <div>
.
The key/value pairs of the object literal are translated
into the attributes of the new element.
The result is a DOM element created by jQuery that you will assign to a
new variable called $div. This will not be an
instance variable (that is, it is just $div and not this.$div
).
It is prefixed with the $
to denote that it is not
a plain DOM element, but one that jQuery created a reference to.
Make it so in checklist.js.
... function Row(coffeeOrder) {// Constructor code will go herevar $div = $('<div></div>', { 'data-coffee-order': 'checkbox', 'class': 'checkbox' }); } ...
Notice that your two property names are in single quotation marks. You might assume from this that you should always use single quotes around property names when creating a DOM element using jQuery, but actually that is not the case. Property names that have special characters (like the dash) need to be in quotes, otherwise it is considered a syntax error. Valid characters that can be used in a property name (or a variable name) without single quotes are the letters of the alphabet, numerical digits, the underscore (_), and the dollar sign ($).
'class'
is in single quotes because “class” is a JavaScript-reserved word,
so single quotes are needed to prevent the browser from reading it as JavaScript (which
would also result in a syntax error).
Next, create the <label>
element in checklist.js with the $ function
but without an object argument. It does not need any extra attributes.
... function Row(coffeeOrder) { var $div = $('<div></div>', { 'data-coffee-order': 'checkbox', 'class': 'checkbox' }); var $label = $('<label></label>'); } ...
Now, create the <input>
element for the
checkbox by calling the $ function and
passing it the HTML for an <input>
tag.
For the second argument, specify that the type should be a
checkbox
and that the value
should be the email address of the customer.
Because none of these property names use special characters,
you do not need to put them in single quotes.
... function Row(coffeeOrder) { var $div = $('<div></div>', { 'data-coffee-order': 'checkbox', 'class': 'checkbox' }); var $label = $('<label></label>'); var $checkbox = $('<input></input>', { type: 'checkbox', value: coffeeOrder.emailAddress }); } ...
By setting the value
to the customer’s email
address, you are associating the checkbox with the customer’s
coffee order.
Later, when you add the click
handler, you can identify which
coffee order was clicked based on the email address in the
value
attribute.
The last thing to create is the text description that will
be displayed next to the checkbox. You will build a string for
this by concatenating the
pieces using the +=
operator.
Create a variable called description in checklist.js. Set it
to the size
property of the order, then add a
comma and a space. If a flavor was provided, concatenate it using
+=
.
Then concatenate the coffee
,
emailAddress
, and strength
values. The emailAddress
should be wrapped in
parentheses and the strength
should be in
brackets and followed by the letter “x.”
(The parentheses and brackets are not for syntactic purposes, just for formatting the text.)
... function Row(coffeeOrder) { ... var $checkbox = $('<input></input>', { type: 'checkbox', value: coffeeOrder.emailAddress }); var description = coffeeOrder.size + ' '; if (coffeeOrder.flavor) { description += coffeeOrder.flavor + ' '; } description += coffeeOrder.coffee + ', '; description += ' (' + coffeeOrder.emailAddress + ')'; description += ' [' + coffeeOrder.strength + 'x]'; } ...
The +=
concatenation operator does addition and assignment
in one step. That means that the following two lines of code
are equivalent:
description += coffeeOrder.flavor + ' '; description = description + coffeeOrder.flavor + ' ';
You now have all the individual parts of the checklist item and are ready to append them to one another (Figure 11.5).
You will do this in three steps:
Append the $checkbox to the $label
Append the description to the $label
Append the $label to the $div
More generally, you will build the subtree by working from left to right, bottom to top. This approach is similar to how you developed your CSS for Ottergram in Chapter 3, by beginning with the smallest, innermost elements and working your way up.
In checklist.js, use the jQuery append method to connect the elements together. This method accepts either a DOM element or a jQuery-wrapped collection and adds it as a child element.
... function Row(coffeeOrder) { ... description += coffeeOrder.coffee + ', '; description += ' (' + coffeeOrder.emailAddress + ')'; description += ' [' + coffeeOrder.strength + 'x]'; $label.append($checkbox); $label.append(description); $div.append($label); } ...
Your Row constructor can now create and assemble
the subtree of elements using the coffee order data passed in.
However, because Row will be used as a constructor
and not as a regular function, it cannot simply return this
subtree. (In fact, constructors should never have
a return statement; JavaScript automatically returns a value for you when you
use the keyword new
with a constructor.)
Instead, make the subtree available as a property of the instance by assigning it to this.$element in checklist.js. (This name was chosen just to follow the convention used with your other constructors; it does not have any special meaning by itself.)
... function Row(coffeeOrder) { ... $label.append($checkbox); $label.append(description); $div.append($label); this.$element = $div; } ...
The Row constructor is ready for work. It can build up the DOM subtree necessary to represent an individual coffee order with a checkbox. It holds on to that DOM representation in an instance variable.
3.145.70.38