Scrollbars - The jQuery API - Web Development with jQuery (2015)

Web Development with jQuery (2015)

Part I. The jQuery API

Chapter 10. Scrollbars

jQuery provides functionality to interact with scrollable DOM elements. As discussed in the preceding chapter, plugins can extend jQuery with even more capability. Even a quick web search results in an array of plugins that tout their scrolling merits. Although these plugins can be useful for specific purposes, jQuery handles most common needs.

This chapter focuses on the interactions provided by the jQuery core framework. These interactions include determining the current scrollbar position of a scrollable element as well as setting the scrollbar position. As expected, you could accomplish these tasks using a combination of JavaScript and DOM attributes; however, as usual, jQuery simplifies and standardizes these items.

Getting the Position of a Scrollbar

Like most of jQuery's functionality, getting the position of a scrollbar is simple:

$('div#aScrollableElement').scrollTop();

$('div#aScrollableElement').scrollLeft();

As demonstrated by the preceding lines of code, scroll position involves two dimensions: vertical and horizontal. Vertical scroll position is measured in pixels from the top, and horizontal scroll position is measured in pixels from the left.

To showcase the output from these functions, the following example attaches a scroll event handler that displays the scroll position during scrolling. You begin with the XHTML5 base, which is presented in the following markup (Example 10-1 in the code downloads at www.wrox.com/go/webdevwithjquery):

<!DOCTYPE HTML>

<html xmlns='http://www.w3.org/1999/xhtml'>

<head>

<meta http-equiv='content-type'

content='application/xhtml+xml; charset=utf-8' />

<meta http-equiv='content-language' content='en-us' />

<title>Scrollbar Position</title>

<script src='../jQuery.js'></script>

<script src='Example 10-1.js'></script>

<link href='Example 10-1.css' rel='stylesheet' />

</head>

<body>

<div id='container'>

<div class='filler'>

Lorem ipsum dolor sit amet, consectetur adipisicing elit,

sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris

nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in

reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla

pariatur. Excepteur sint occaecat cupidatat non proident, sunt in

culpa qui officia deserunt mollit anim id est laborum.

</div>

</div>

<div class='status-bar'>

<span id='vertical-scroll-label'>

Current Vertical Scrollbar Position:

</span>

<span id='vertical-scroll-value'>0</span>

</div>

<div class='status-bar'>

<span id='horizontal-scroll-label'>

Current Horizontal Scrollbar Position:

</span>

<span id='horizontal-scroll-value'>0</span>

</div>

</body>

</html>

The following CSS sets up the styling for the scrollbar example:

html,

body {

width: 100%;

height: 100%;

}

body {

font: 12px "Lucida Grande", Arial, sans-serif;

background: rgb(189, 189, 189);

color: rgb(50, 50, 50);

margin: 0;

padding: 0;

}

div#container {

border: 1px solid rgb(64, 64, 64);

background: #fff;

padding: 5px;

margin: 0 20px 0 20px;

width: 200px;

height: 100px;

overflow: auto;

}

div.filler {

margin: 10px;

padding: 5px;

width: 400px;

height: 150px;

background: rgb(200, 200, 255);

}

div.status-bar {

margin: 5px 20px 5px 20px;

}

The preceding XHTML and CSS are combined with the following JavaScript:

$(document).ready(

function()

{

$('div#container')

.scroll(

function()

{

$('span#vertical-scroll-value')

.text($(this).scrollTop());

$('span#horizontal-scroll-value')

.text($(this).scrollLeft());

}

);

}

);

The preceding source code results in the document that you see in Figure 10.1.

image

Figure 10.1

In the preceding example, you created a scrollable <div> element with content larger than its container. When you scroll that container to view its content, the scrollbar positions are retrieved and displayed on the page.

The markup in this example is straightforward. The scrollable content is contained in the <div> element with id name container; this container element is needed so that the size of the contents exceeds the size of the container to allow for scrolling.

<div id='container'>

Within that container, the <div> element with the class name filler simply contains sample text to help show movement when the container is scrolled.

<div class='filler'>

Lorem ipsum dolor sit amet, consectetur adipisicing elit,

sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris

nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in

reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla

pariatur. Excepteur sint occaecat cupidatat non proident, sunt in

culpa qui officia deserunt mollit anim id est laborum.

</div>

In addition to the scrollable content, the markup includes two <div> elements with the class name status-bar for use in displaying the scrollbar position values.

<div class='status-bar'>

<span id='vertical-scroll-label'>

Current Vertical Scrollbar Position:

</span>

<span id='vertical-scroll-value'>0</span>

</div>

<div class='status-bar'>

<span id='horizontal-scroll-label'>

Current Horizontal Scrollbar Position:

</span>

<span id='horizontal-scroll-value'>0</span>

</div>

The style sheet presents this markup in such a fashion that the contents of the container require more space than the container itself so that the browser can provide useful scrollbars for the container. The following reviews each rule in the style sheet and explains why each is needed.

The <html> and <body> element are each given 100 percent width and height so that these both automatically take up the entire viewport.

html,

body {

width: 100%;

height: 100%;

}

In the next rule, you give the document a Lucida Grande font, which is a Mac font commonly used in Mac applications. If that font isn't available, you can fall back on Arial, which is present in Windows; otherwise, if that font is not present, you fall back on the generic sans-serif font. The background is set to a shade of gray. The font color is set to a dark gray, and finally, the default padding and margin are removed from the <body> element, which is necessary to avoid scrollbars that would appear on the viewport due to the application of 100 percent width and height with the preceding stylesheet rule.

body {

font: 12px "Lucida Grande", Arial, sans-serif;

background: rgb(189, 189, 189;

color: rgb(50, 50, 50);

margin: 0;

padding: 0;

}

The next rule positions the <div> element with id name container, which contains the scrollable content. This <div> element is sized to a rather small 200 pixels by 100 pixels, and a small amount of padding and horizontal margin is applied. The background is set to white; there is a dark gray border placed around the container; and finally, the overflow: auto; declaration is added so that scrollbars appear when this element's contents exceed its specified dimensions.

div#container {

border: 1px solid rgb(64, 64, 64);

background: #fff;

padding: 5px;

margin: 0 20px 0 20px;

width: 200px;

height: 100px;

overflow: auto;

}

The next rule sets the presentation of the <div> element with the class name filler, setting a small amount of margin and padding as well as a light blue background. More important, the element is sized to 400 pixels by 150 pixels so that it is larger than its container.

div.filler {

margin: 10px;

padding: 5px;

width: 400px;

height: 150px;

background: rgb(200, 200, 255);

}

The next rule simply adds a small amount of margin to the <div> elements with the class name status-bar to add a small amount of space between the elements and to align with the scrollable container.

div.status-bar {

margin: 5px 20px 5px 20px;

}

The JavaScript for this example is, as usual, succinct. You start by adding a handler function for the scroll event.

$('div#container')

.scroll(

Inside the scroll event, you write some logic for updating the status bars by getting the scrollbar positions. First, you get the scrollTop() value from the scrolled element and use its value to set the innerText of the span#vertical-scroll-value element. Then you similarly get the scrollLeft() value from the scrolled element and use that value to set the innerText of the span#horizontal-scroll-value element.

function()

{

$('span#vertical-scroll-value')

.text($(this).scrollTop());

$('span#horizontal-scroll-value')

.text($(this).scrollLeft());

}

With this minimal amount of jQuery, you have retrieved the scrollbar position values of a DOM element while it is being scrolled and updated additional DOM elements to display those values. The remaining examples in this chapter build upon this structure that you created.

Scrolling to a Particular Element within a Scrolling <div>

As discussed in the introduction to this chapter, and as is typical with jQuery, the same method used to get the value can also be used to set the value. Therefore, setting the scrollbar positions of a scrollable element is as easy as

$('div#aScrollableElement').scrollTop(100);

$('div#aScrollableElement').scrollLeft(100);

Again, the values should be specified in pixels when setting the scrollbar positions; as a result, you must calculate the pixel values if you want to scroll directly to an element within a scrollable container. Example 10-2 shows multiple elements within a scrolling <div>element and the code needed to scroll directly to each:

<!DOCTYPE HTML>

<html xmlns='http://www.w3.org/1999/xhtml'>

<head>

<meta http-equiv='content-type'

content='application/xhtml+xml; charset=utf-8' />

<meta http-equiv='content-language' content='en-us' />

<title>Scrollbar Position</title>

<script src='../jQuery.js'></script>

<script src='Example 10-2.js'></script>

<link href='Example 10-2.css' rel='stylesheet' />

</head>

<body>

<div id='container'>

<div class='filler'>

Lorem ipsum dolor sit amet, consectetur adipisicing elit,

sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris

nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in

reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla

pariatur. Excepteur sint occaecat cupidatat non proident, sunt in

culpa qui officia deserunt mollit anim id est laborum.

</div>

<div id='block1' class='block'>Block 1</div>

<div id='block2' class='block'>Block 2</div>

</div>

<div class='button-bar'>

<button class='block-button' data-block='block1'>

Go to Block 1

</button>

<button class='block-button' data-block='block2'>

Go to Block 2

</button>

</div>

<div class='status-bar'>

<span id='vertical-scroll-label'>

Current Vertical Scrollbar Position:

</span>

<span id='vertical-scroll-value'>0</span>

</div>

<div class='status-bar'>

<span id='horizontal-scroll-label'>

Current Horizontal Scrollbar Position:

</span>

<span id='horizontal-scroll-value'>0</span>

</div>

</body>

</html>

The preceding HTML is combined with the following CSS:

html,

body {

width: 100%;

height: 100%;

}

body {

font: 12px "Lucida Grande", Arial, sans-serif;

background: rgb(189, 189, 189);

color: rgb(50, 50, 50);

margin: 0;

padding: 0;

}

div#container {

border: 1px solid rgb(64, 64, 64);

background: #fff;

padding: 5px;

margin: 0 20px 0 20px;

width: 200px;

height: 100px;

overflow: auto;

}

div.filler {

margin: 10px;

padding: 5px;

width: 400px;

height: 150px;

background: rgb(200, 200, 255);

}

div.status-bar,

div.button-bar {

margin: 5px 20px 5px 20px;

}

div.block {

margin: 10px;

padding: 5px;

width: 400px;

height: 70px;

background-color: rgb(255, 140, 0);

}

Finally, you apply the following JavaScript, which extends Example 10-1 with new code that enables the click event handlers to set scrollbar positions.

$(document).ready(

function()

{

$('div#container')

.scroll(

function()

{

$('span#vertical-scroll-value')

.text($(this).scrollTop());

$('span#horizontal-scroll-value')

.text($(this).scrollLeft());

}

);

$('button.block-button')

.click(

function()

{

$('div#container')

.scrollTop($('div#' + $(this).data().block).offset().top

- $('div#container').offset().top

+ $('div#container').scrollTop())

.scrollLeft($('div#' + $(this).data().block).offset().left

- $('div#container').offset().left

+ $('div#container').scrollLeft());

}

);

}

);

The preceding source code gives you output, as shown in Figure 10.2, in Safari on Mac OS X.

image

Figure 10.2

In the preceding example, you added two elements within the scrollable container, added buttons to reference those new elements, and wired the click event of these buttons to set the scrollbar positions of the scrollable container.

The two new <div> elements were added within the scrollable container, that is, the <div> element with id name container.

<div id='block1' class='block'>Block 1</div>

<div id='block2' class='block'>Block 2</div>

The buttons and their containing <div> element were added outside the scrollable container before the status bars. The data-block attributes of these buttons were used within the click event handlers to reference the appropriate block for the clicked button.

<div class='button-bar'>

<button class='block-button' data-block='block1'>Go to Block 1</button>

<button class='block-button' data-block='block2'>Go to Block 2</button>

</div>

To set up the document for this example, you added a rule to the style sheet for the new elements. In addition to size and spacing, the elements are set to a dark orange color to offset the filler text.

div.block {

margin: 10px;

padding: 5px;

width: 400px;

height: 70px;

background-color: rgb(255, 140, 0);

}

In addition, you reused the status-bar style rules for the new button-bar element to maintain consistency.

div.status-bar,

div.button-bar {

margin: 5px 20px 5px 20px;

}

Next, you added a click event handler for the new buttons to scroll the scrollable container to the referenced elements within the scrollable container.

$('button.block-button')

.click(

function()

{

$('div#container')

.scrollTop($('div#' + $(this).data().block).offset().top

- $('div#container').offset().top

+ $('div#container').scrollTop())

.scrollLeft($('div#' + $(this).data().block).offset().left

- $('div#container').offset().left

+ $('div#container').scrollLeft());

}

);

Within this event handler, you set the scrollTop() value for the <div> element with id name container using calculated values. You began with the value of the top edge of the block element specified by the button's data-block attribute (relative to the top of the document).

$('div#' + $(this).data().block).offset().top

From that value, you subtracted the value of the top edge of the scrollable container.

$('div#container').offset().top

Finally, you added back in the current scroll position of the scrollable container, which has an impact on the first value in this calculation.

$('div#container').scrollTop()

You followed the same pattern to set the horizontal scrollbar position using scrollLeft().

Scrolling to the Top

The preceding example demonstrated the functionality of setting the scrollbar positions using calculated values. Scrolling to the top of a scrollable container is a specialized case in which the value to be set is zero. To scroll the container to its top leftmost content, you need only one line of jQuery code:

$('div#aScrollableElement').scrollTop(0).scrollLeft(0);

jQuery's scrollTop() and scrollLeft() are fairly tolerant of values outside the logical bounds. This tolerance can be useful when the values are calculated. For instance, fractional values are truncated; most values greater than the maximum possible scrollbar position result in the maximum valid value; and negative or otherwise invalid values result in zero. As a result, the following line also scrolls the container to its top leftmost content:

$('div#aScrollableElement').scrollTop('red').scrollLeft('blue');

Scrolling a container to its bottom rightmost content can be accomplished by calculating values larger than the maximum possible values or by simply using a value sufficiently large to be reasonably sure of exceeding the maximum but sufficiently small to be valid. Both approaches are demonstrated in the following lines:

$('div#aScrollableElement').scrollTop($('div#aScrollableElement')

.prop('scrollHeight')).scrollLeft($('div#aScrollableElement')

.prop('scrollWidth'));

$('div#aScrollableElement').scrollTop(999999999).scrollLeft(999999999);

Although the last approach might leave you scratching your head, it is technically more efficient than selecting elements from the DOM and reading their properties.

Summary

In this chapter, you learned how to retrieve and update the scrollbar positions of scrollable content.

Throughout this chapter, you worked on building a page to display the current scroll positions as content was scrolled, discovering in the process the event handler you can specify to execute code during a scrolling operation. You extended the page to set the scroll positions based on elements within the scrollable content.

Finally, you learned how to scroll a container to the limits of its content, including the most common case of scrolling to the top, and you became acquainted with some of the nuances of jQuery's scrollTop() and scrollLeft() methods for values outside the expected ranges.

Exercises

1. If you want to retrieve the current scrollbar positions for a scrollable element, which jQuery functions would you use?

2. If you want to scroll the top of a particular element into view within its scrollable container, which three coordinates are needed?

3. Write the function call that you would use to scroll a scrollable element to its top.

4. Describe two general approaches to scroll a scrollable element to its bottom.

5. If an invalid value is specified when setting scrollTop() or scrollLeft(), what value is used by the function?