76
tags) that is broken across two lines. Its rectangles consist of the right-hand portion of
the first line and the left-hand portion of the second line (assuming left-to-right text).
If you call
getBoundingClientRect()
on an inline element, it returns the “bounding
rectangle” of the individual rectangles. For the
<i>
element described above, the
bounding rectangle would include the entire width of both lines.
If you want to query the individual rectangles of inline elements, call the
getClientRects()
method to obtain a read-only array-like object whose elements are
rectangle objects like those returned by
getBoundingClientRect()
.
We’ve seen that DOM methods like
getElementsByTagName()
return “live” results that
are updated as the document changes. The rectangle objects (and rectangle object lists)
returned by
getBoundingClientRect()
and
getClientRects()
are not live. They are static
snapshots of the visual state of document when the methods are called. They are not
updated when the user scrolls or resizes the browser window.
15.8.3 Determining the Element at a Point
The
getBoundingClientRect()
method allows us to determine the current position of
an element in a viewport. Sometimes we want to go in the other direction and determine
which element is at a given location in the viewport. You can determine this with the
elementFromPoint()
method of the Document object. Pass X and Y coordinates (using
viewport coordinates, not document coordinates) and this method returns an Element
object that is at the specified position. At the time of this writing, the algorithm for
selecting the element is not specified, but the intent of this method is that it returns the
innermost and uppermost (see the CSS
z-index
attribute in §16.2.1.1) element at that
point. If you specify a point that is outside of the viewport,
elementFromPoint()
will
return
null
even if that point would be perfectly valid when converted to document
coordinates.
elementFromPoint()
seems like a very useful method, and the obvious use case is passing
the coordinates of the mouse pointer to determine which element the mouse is over.
As we’ll learn in Chapter 17, however, mouse event objects already include this infor-
mation in their
target
property. In practice, therefore,
elementFromPoint()
is not com-
monly used.
15.8.4 Scrolling
Example 15-8 showed how to query the scrollbar positions for a browser window. The
scrollLeft
and
scrollTop
properties used in that example can be set to make the
browser scroll, but there is an easier way that has been supported since the earliest days
of JavaScript. The
scrollTo()
method of the Window object (and its synonym
scroll()
) takes the X and Y coordinates of a point (in document coordinates) and sets
these as the scrollbar offsets. That is, it scrolls the window so that the specified point
is in the upper left corner of the viewport. If you specify a point that is too close to the
bottom or too close to the right edge of the document, the browser will move it as close
as possible to the upper left corner but won’t be able to get it all the way there. The
15.8 Document and Element Geometry and Scrolling | 393
Client-Side
JavaScript
81
following code scrolls the browser so that the bottom-most page of the document is
visible:
// Get the height of the document and viewport. offsetHeight is explained below.
var documentHeight = document.documentElement.offsetHeight;
var viewportHeight = window.innerHeight; // Or use getViewportSize() above
// And scroll so the last "page" shows in the viewport
window.scrollTo(0, documentHeight - viewportHeight);
The
scrollBy()
method of the Window is similar to
scroll()
and
scrollTo()
, but its
arguments are relative and are added to the current scrollbar offsets. Speed readers
might like a bookmarklet (§13.2.5.1) like this one, for example:
// Scroll 10 pixels down every 200 ms. Note there is no way to turn this off!
javascript:void setInterval(function() {scrollBy(0,10)}, 200);
Often, instead of scrolling to a numeric location in document, we just want to scroll so
that a certain element in the document is visible. You could compute the position of
the element with
getBoundingClientRect()
, convert that position to document coordi-
nates, and then use the
scrollTo()
method, but it is easier to just call the
scrollInto
View()
method on the desired HTML element. This method ensures that the element
on which it is invoked is visible in the viewport. By default, it tries to put the top edge
of the element at or near the top of the viewport. If you pass
false
as the only argument,
it will try to put the bottom edge of the element at the bottom of the viewport. The
browser will also scroll the viewport horizontally as needed to make the element visible.
The behavior of
scrollIntoView()
is similar to what the browser does when you set
window.location.hash
to the name of a named anchor (an
<a name="">
element).
15.8.5 More on Element Size, Position and Overflow
The
getBoundingClientRect()
method is defined in all current browsers, but if you need
to support an older generation of browsers, you can’t rely on this method and must use
older techniques for determining element size and position. Element size is easy: the
readonly
offsetWidth
and
offsetHeight
properties of any HTML element return its on
screen size, in CSS pixels. The returned sizes include the element border and padding
but not margins.
All HTML elements have
offsetLeft
and
offsetTop
properties that return the X and Y
coordinates of the element. For many elements, these values are document coordinates
and directly specify the position of the element. But for descendants of positioned el-
ements and for some other elements, such as table cells, these properties return coor-
dinates that are relative to an ancestor element rather than the document. The
offset
Parent
property specifies which element the properties are relative to. If
offsetParent
is null, the properties are document coordinates. In general, therefore, computing the
position of an element
e
using
offsetLeft
and
offsetTop
requires a loop:
function getElementPosition(e) {
var x = 0, y = 0;
while(e != null) {
x += e.offsetLeft;
394 | Chapter 15: Scripting Documents
109
y += e.offsetTop;
e = e.offsetParent;
}
return {x:x, y:y};
}
By looping through the
offsetParent
chain and accumulating offsets, this function
computes the document coordinates of the specified element. (Recall that
getBounding
ClientRect()
returns viewport coordinates instead.) This is not the final word on ele-
ment positioning, however—this
getElementPosition()
function does not always com-
pute the correct values, and we’ll see how to fix it below.
In addition to the set of
offset
properties, all document elements define two other sets
of properties, one whose names begin with
client
and one whose names begin with
scroll
. That is, every HTML element has all of the following properties:
offsetWidth clientWidth scrollWidth
offsetHeight clientHeight scrollHeight
offsetLeft clientLeft scrollLeft
offsetTop clientTop scrollTop
offsetParent
In order to understand these
client
and
scroll
properties, you need to know that the
content of an HTML element may be larger than the content box allocated to hold that
content, and that therefore individual elements may have scrollbars (see the CSS
over
flow
attribute in §16.2.6). The content area is a viewport, like the browser window is,
and when the content is larger than the viewport, we need to take an element’s scrollbar
position into account.
clientWidth
and
clientHeight
are like
offsetWidth
and
offsetHeight
except that they
do not include the border size, only the content area and its padding. Also, if the browser
has added scrollbars between the padding and the border,
clientWidth
and
clientHeight
do not include the scrollbar in their returned value. Note that
client
Width
and
clientHeight
always return 0 for inline elements like
<i>
,
<code>
, and
<span>
.
clientWidth
and
clientHeight
were used in the
getViewportSize()
method of Exam-
ple 15-9. As a special case, when these properties are queried on the root element of a
document (or the body element in quirks mode), they return the same values as the
innerWidth
and
innerHeight
properties of the window.
The
clientLeft
and
clientTop
properties are not very useful: they return the horizontal
and vertical distance between the outside of an element’s padding and the outside of
its border. Usually these values are just the width of the left and top borders. If an
element has scrollbars, however, and if the browser places those scrollbars on the left
or top (which would be unusual),
clientLeft
and
clientTop
also include the scrollbar
width. For inline elements,
clientLeft
and
clientTop
are always 0.
scrollWidth
and
scrollHeight
are the size of an element’s content area plus its padding
plus any overflowing content. When the content fits within the content area without
overflow, these properties are the same as
clientWidth
and
clientHeight
. But when
15.8 Document and Element Geometry and Scrolling | 395
Client-Side
JavaScript
77
there is overflow, they include the overflowing content and return values larger than
clientWidth
and
clientHeight
.
Finally,
scrollLeft
and
scrollTop
give the scrollbar positions of an element. We queried
them on the root element of the document in the
getScrollOffsets()
method (Exam-
ple 15-8), but they are also defined on any element. Note that
scrollLeft
and
scroll
Top
are writable properties and you can set them to scroll the content within an element.
(HTML elements do not have a
scrollTo()
method like the Window object does.)
When a document contains scrollable elements with overflowing content, the
getEle
mentPosition()
method defined above does not work correctly because it does not take
scrollbar position into account. Here is a modified version that subtracts scrollbar po-
sitions from the accumulated offsets and, in so doing, converts the returned position
from document coordinates to viewport coordinates:
function getElementPos(elt) {
var x = 0, y = 0;
// Loop to add up offsets
for(var e = elt; e != null; e = e.offsetParent) {
x += e.offsetLeft;
y += e.offsetTop;
}
// Loop again, through all ancestor elements to subtract scroll offsets.
// This subtracts the main scrollbars, too, and converts to viewport coords.
for(var e=elt.parentNode; e != null && e.nodeType == 1; e=e.parentNode) {
x -= e.scrollLeft;
y -= e.scrollTop;
}
return {x:x, y:y};
}
In modern browsers, this
getElementPos()
method returns the same position values as
getBoundingClientRect()
does (but is much less efficient). Theoretically, a function
such as
getElementPos()
could be used in browsers that do not support
getBoundingClientRect()
. In practice, however, browsers that do not support
getBoundingClientRect()
have a lot of element positioning incompatibilities and a
function as simple as this one will not work reliably. Practical client-side libraries like
jQuery include functions for computing element position that augment this basic po-
sition computation algorithm with a number of browser-specific bug fixes. If you need
to compute element position and need your code to work in browsers that do not
support
getBoundingClientRect()
, you should probably use a library like jQuery.
15.9 HTML Forms
The HTML
<form>
element, and the various form input elements, such as
<input>
,
<select>
, and
<button>
, have an important place in client-side programming. These
HTML elements date from the very beginning of the Web and predate JavaScript itself.
HTML forms are the mechanism behind the first generation of web applications, which
required no JavaScript at all. User input is gathered in form elements; form submission
396 | Chapter 15: Scripting Documents
.NET PDF SDK | Read & Processing PDF files by this .NET Imaging PDF Reader Add-on. Include extraction of text, hyperlinks, bookmarks and metadata; Annotate and redact in PDF documents; Fully support all
pdf link to email; add hyperlink to pdf online PDF Image Viewer| What is PDF advanced capabilities, such as text extraction, hyperlinks, bookmarks and Note: PDF processing and conversion is excluded in NET Imaging SDK, you may add it on
add links to pdf online; check links in pdf
80
sends that input to the server; the server processes the input and generates a new HTML
page (usually with new form elements) for display by the client.
HTML form elements are still a great way to gather input from the user, even when
form data is processed entirely by client-side JavaScript and never submitted to the
server. With server-side programs, a form isn’t useful unless it has a Submit button. In
client-side programming, on the other hand, a Submit button is never necessary (though
it may still be useful). Server-side programs are based on form submissions—they
process data in form-sized chunks—and this limits their interactivity. Client-side pro-
grams are event based—they can respond to events on individual form elements—and
this allows them to be much more responsive. A client-side program might validate the
user’s input as she types it, for example. Or it might respond to a click on a checkbox
by enabling a set of options that are only meaningful when that box is checked.
The subsections that follow explain how to do these kinds of things with HTML forms.
Forms are composed of HTML elements, just like any other part of an HTML docu-
ment, and you can manipulate them with the DOM techniques already explained in
this chapter. But form elements were the first ones to be made scriptable, in the earliest
days of client-side programming, and they also support some APIs that predate
the DOM.
Note that this section is about scripting HTML forms, not about the HTML itself. It
assumes that you are already somewhat familiar with the HTML elements (
<input>
,
<textarea>
,
<select>
, and so on) used to define those forms. Nevertheless, Ta-
ble 15-1 is a quick reference to the most commonly used form elements. You can read
more about the form and form element APIs in Part IV, under the entries
Form
,
Input
,
Option
,
Select
, and
TextArea
.
Table 15-1. HTML form elements
HTML element
Type property
Event handler
Description and events
<input type="button"> or
<button type="button">
“button”
onclick
A push button
<input type="checkbox">
“checkbox”
onchange
A toggle button without radio button
behavior
<input type="file">
“file”
onchange
An input field for entering the name of a file
to upload to the web server;
value
property
is read-only
<input type="hidden">
“hidden”
none
Data submitted with the form but not visible
to the user
<option>
none
none
A single item within a Select object; event
handlers are on the Select object, not on
individual Option objects
<input type="password">
“password”
onchange
An input field for password entry—typed
characters are not visible
15.9 HTML Forms | 397
Client-Side
JavaScript
86
HTML element
Type property
Event handler
Description and events
<input type="radio">
“radio”
onchange
A toggle button with radio button
behavior—only one selected at a time
<input type="reset"> or
<button type="reset">
“reset”
onclick
A push button that resets a form
<select>
“select-one”
onchange
A list or drop-down menu from which one
item may be selected (also see <option>)
<select multiple>
“select-multiple” onchange
A list from which multiple items may be
selected (also see <option>)
<input type="submit"> or
<button type="submit">
“submit”
onclick
A push button that submits a form
<input type="text">
“text”
onchange
A single-line text entry field; the default
<input> element it
type
attribute is
omitted or unrecognized
<textarea>
“textarea”
onchange
A multiline text entry field
15.9.1 Selecting Forms and Form Elements
Forms and the elements they contain can be selected from a document using standard
methods like
getElementById()
and
getElementsByTagName()
:
var fields = document.getElementById("address").getElementsByTagName("input");
In browsers that support
querySelectorAll()
, you might select all radio buttons, or all
elements with the same name, from a form with code like this:
// All radio buttons in the form with id "shipping"
document.querySelectorAll('#shipping input[type="radio"]');
// All radio buttons with name "method" in form with id "shipping"
document.querySelectorAll('#shipping input[type="radio"][name="method"]');
As described in §14.7, §15.2.2, and §15.2.3, however, a
<form>
element with a
name
or
id
attribute can be selected in a number of other ways. A
<form>
with a
name="address"
attribute can be selected in any of these ways:
window.address // Brittle: do not use
document.address // Only works for forms with name attribute
document.forms.address // Explicit access to a form with name or id
document.forms[n] // Brittle: n is the form's numerical position
§15.2.3 explained that
document.forms
is an HTMLCollection object that allows form
elements to be selected by numerical order, by
id
, or by
name
. Form objects themselves
act like HTMLCollections of form elements and can be indexed by name or number.
If a form with name “address” has a first element with name “street”, you can refer to
that form element with any of these expressions:
document.forms.address[0]
document.forms.address.street
document.address.street // only for name="address", not id="address"
398 | Chapter 15: Scripting Documents
81
If you want to be explicit about selecting a form element, you can index the
elements
property of the form object instead:
document.forms.address.elements[0]
document.forms.address.elements.street
The
id
attribute is the generally preferred way to name specific document elements.
The
name
attribute, however, has a special purpose for HTML form submission, and is
much more commonly used with forms than with other elements. It is typical for groups
of related checkboxes and mandatory for mutually exclusive groups of radioboxes to
share a value of the
name
attribute. Remember that when you index an HTMLCollection
with a name and more than one element shares that name, the returned value is an
array-like object that contains all matching elements. Consider this form that contains
radio buttons for selecting a shipping method:
<form name="shipping">
<fieldset><legend>Shipping Method</legend>
<label><input type="radio" name="method" value="1st">First-class</label>
<label><input type="radio" name="method" value="2day">2-day Air</label>
<label><input type="radio" name="method" value="overnite">Overnight</label>
</fieldset>
</form>
With this form, you might refer to the array of radio button elements like this:
var methods = document.forms.shipping.elements.method;
Note that
<form>
elements have an HTML attribute and corresponding JavaScript
property named “method”, so in this case, we must use the
elements
property of the
form instead of directly accessing the
method
property. In order to determine which
shipping method the user has selected, we’d loop through the form elements in the
array and check the
checked
property of each:
var shipping_method;
for(var i = 0; i < methods.length; i++)
if (methods[i].checked) shipping_method = methods[i].value;
We’ll see more about the properties, such as
checked
and
value
, of form elements in
the next section.
15.9.2 Form and Element Properties
The
elements[]
array described above is the most interesting property of a Form object.
The remaining properties of the Form object are of less importance. The
action
,
encoding
,
method
, and
target
properties correspond directly to the
action
,
encoding
,
method
, and
target
attributes of the
<form>
element. These properties and attributes are
all used to control how form data is submitted to the web server and where the results
are displayed. Client-side JavaScript can set the value of these properties, but they are
only useful when the form is actually submitted to a server-side program.
In the days before JavaScript, a form was submitted with a special-purpose Submit
button, and form elements had their values reset with a special-purpose Reset button.
15.9 HTML Forms | 399
Client-Side
JavaScript
92
The JavaScript Form object supports two methods,
submit()
and
reset()
, that serve
the same purpose. Invoking the
submit()
method of a Form submits the form, and
invoking
reset()
resets the form elements.
All (or most) form elements have the following properties in common. Some elements
have other special-purpose properties that are described later when various types of
form elements are considered individually:
type
A read-only string that identifies the type of the form element. For form elements
that are defined by an
<input>
tag, this is simply the value of the
type
attribute.
Other form elements (such as
<textarea>
and
<select>
) define a
type
property so
that they can easily be identified by the same test that distinguishes between
<input>
elements. The second column of Table 15-1 lists the value of this property
for each form element.
form
A read-only reference to the Form object in which the element is contained, or
null
if the element is not contained within a
<form>
element.
name
A read-only string specified by the HTML
name
attribute.
value
A read/write string that specifies the “value” contained or represented by the form
element. This is the string that is sent to the web server when the form is submitted,
and it is only sometimes of interest to JavaScript programs. For Text and Textarea
elements, this property contains the text that the user entered. For button elements
created with an
<input>
tag (but not those created with a
<button>
tag) this property
specifies the text displayed within the button. For radio and checkbox elements,
however, the
value
property is not edited or displayed to the user in any way. It is
simply a string set by the HTML
value
attribute. It is intended for use in form
submission, but it can also be a useful way to associate extra data with a form
element. The
value
property is discussed further in the sections on the different
categories of form elements, later in this chapter.
15.9.3 Form and Element Event Handlers
Each Form element has an
onsubmit
event handler to detect form submission and an
onreset
event handler to detect form resets. The
onsubmit
handler is invoked just before
the form is submitted; it can cancel the submission by returning
false
. This provides
an opportunity for a JavaScript program to check the user’s input for errors in order to
avoid submitting incomplete or invalid data over the network to a server-side program.
Note that the
onsubmit
handler is triggered only by a genuine click on a Submit button.
Calling the
submit()
method of a form does not trigger the
onsubmit
handler.
The
onreset
event handler is similar to the
onsubmit
handler. It is invoked just before
the form is reset, and it can prevent the form elements from being reset by returning
400 | Chapter 15: Scripting Documents
83
false
. Reset buttons are rarely necessary in forms, but if you have one, you might want
to make the user confirm the reset:
<form...
onreset="return confirm('Really erase ALL input and start over?')">
...
<button type="reset">Clear and Start Over</button>
</form>
Like the
onsubmit
handler,
onreset
is triggered only by a genuine Reset button. Calling
the
reset()
method of a form does not trigger
onreset
.
Form elements typically fire a click or change event when the user interacts with them,
and you can handle these events by defining an
onclick
or
onchange
event handler. The
third column of Table 15-1 specifies the primary event handler for each form element.
In general, form elements that are buttons fire a click event when activated (even when
this activation happens through the keyboard rather than via an actual mouse click).
Other form elements fire a change event when the user changes the value represented
by the element. This happens when the user enters text in a text field or selects an option
from a drop-down list. Note that this event is not fired every time the user types a key
in a text field. It is fired only when the user changes the value of an element and then
moves the input focus to some other form element. That is, the invocation of this event
handler indicates a completed change. Radio buttons and checkboxes are buttons that
have a state, and they fire both click and change events; the
change
event is the more
useful of the two.
Form elements also fire a
focus
event when they receive keyboard focus and a
blur
event when they lose it.
An important thing to know about event handlers is that within the code of an event
handler, the
this
keyword refers to the document element that triggered the event (we’ll
talk about this again in Chapter 17). Since elements within a
<form>
element have a
form
property that refers to the containing form, the event handlers of these elements
can always refer to the Form object as
this.form
. Going a step further, this means that
an event handler for one form element can refer to a sibling form element named
x
as
this.form.x
.
15.9.4 Push Buttons
Buttons are one the most commonly used form elements because they provide a clear
visual way to allow the user to trigger some scripted action. A button element has no
default behavior of its own, and it is never useful unless it has an
onclick
event handler.
Buttons defined as
<input>
elements display the plain text of the
value
attribute. But-
tons defined as
<button>
elements display whatever the element content.
Note that hyperlinks provide the same
onclick
event handler that buttons do. Use a
link when the action to be triggered by the
onclick
handler can be conceptualized as
“following a link”; otherwise, use a button.
15.9 HTML Forms | 401
Client-Side
JavaScript
74
Submit and reset elements are just like button elements, but they have default actions
(submitting and resetting a form) associated with them. If the
onclick
event handler
returns
false
, the default action of these buttons is not performed. You can use the
onclick
handler of a submit element to perform form validation, but it is more common
to do this with the
onsubmit
handler of the Form object itself.
Part IV does not include a Button entry. See
Input
for details on all form element push
buttons, including those created with the
<button>
element.
15.9.5 Toggle Buttons
The checkbox and radio elements are toggle buttons, or buttons that have two visually
distinct states: they can be checked or unchecked. The user can change the state of a
toggle button by clicking on it. Radio elements are designed to be used in groups of
related elements, all of which have the same value for the HTML
name
attribute. Radio
elements created in this way are mutually exclusive: when you check one, the one that
was previously checked becomes unchecked. Checkboxes are also often used in groups
that share a
name
attribute, and when you select these elements using the name as a
form property you must remember that you get an array-like object rather than a single
element.
Radio and checkbox elements both define a
checked
property. This read/write boolean
value specifies whether the element is currently checked. The
defaultChecked
property
is a boolean that has the value of the HTML
checked
attribute; it specifies whether the
element is checked when the page is first loaded.
Radio and checkbox elements do not display any text themselves and are typically
displayed with adjacent HTML text (or with an associated
<label>
element.) This
means that setting the
value
property of a checkbox or radio element does not alter the
visual appearance of the element. You can set
value
, but this changes only the string
that is sent to the web server when the form is submitted.
When the user clicks on a toggle button, the radio or checkbox element triggers its
onclick
handlers. If the toggle button changes state as the result of the click, it also
triggers the
onchange
event handlers. (Note, however, that radio buttons that change
state when the user clicks on a different radio button do not fire an
onchange
handler.)
15.9.6 Text Fields
Text input fields are probably the most commonly used element in HTML forms and
JavaScript programs. They allow the user to enter a short, single-line string of text. The
value
property represents the text the user has entered. You can set this property to
specify explicitly the text that should be displayed in the field.
In HTML5, the
placeholder
attribute specifies a prompt to be displayed within the field
before the user enters anything:
Arrival Date: <input type="text" name="arrival" placeholder="yyyy-mm-dd">
402 | Chapter 15: Scripting Documents
Documents you may be interested
Documents you may be interested