Slip Slidin'

As of December 2011, this topic has been archived. As a result, it is no longer actively maintained. For more information, see Archived Content. For information, recommendations, and guidance regarding the current version of Internet Explorer, see Internet Explorer Developer Center.

Michael Wallent
Microsoft Corporation

August 28, 2000

Contents

Basic Design
Implementation
Slidin' Along
Calculating the Thumb
Changing the Right Way
Slidin' Away

We had a discussion here last week about creating a slider using Dynamic HTML (DHTML). A little challenge was thrown down: Could you create a slider that was just as fast as one created by using only Win32 code? As the DHTML Dude, I could not resist such a challenge. I started to code.

Basic Design

I wanted this slider to be easy to use and also flexible. It needed to have good default features, but I wanted to be able to customize it without having to write a lot of script. For these reasons, and because I wanted it to be created declaratively and simply, I chose to use DHTML behaviors.

This particular behavior would be made up of multiple HTML components: a <DIV> for the background, another <DIV> for the middle bar, and a third <DIV> for the thumb. To ensure good encapsulation, and to allow me to create these things declaratively, I chose to create a viewlinked element behavior. Element behaviors and support for the viewlink feature were added in Internet Explorer 5.5.

I wanted to build sliders for both horizontal and vertical orientations, so I created two properties to specify the layout: movelr and moveud.

I also created properties for the background color of the slider and thumb, and for the background image, so that future authors could customize them.

The slider is set up to throw an event every time the slider actually moves. I could have used the onpropertychange notification that comes with changing a property of the slider—but for simplicity, I chose to create a new event, onslide, as well.

The slider exposes its current position through the value property. This is a read/write property, and the values range from 0 to 100.

Implementation

Here's the tag for the slider behavior in the HTML page:

<ie:slider id=Slider1
               movelr=true backgroundColor="red" thumbColor="blue" thumbSize="20px"
               backgroundImage="url(triangle.gif)" backgroundRepeat="no-repeat"
               style="height: 68px; width: 150px"
               onslide="Slider1Output.value = window.event.sliderValue;"/>

Note all of the properties specified above: movelr, backgroundColor, thumbColor, thumbSize, backgroundImage, backgroundRepeat. Also, the new event for the slider, onslide, is set on the tag.

If you're using Internet Explorer 5.5 or higher, you can view the sample. (You can download the latest version of Internet Explorer from the Microsoft Windows Web site.) On the page, you'll see a generic vertical slider and a custom horizontal slider. Try to move the thumbs, and note the value changes in the input box. You can also enter a value in the input box, and select the Change button to see the slider snap to the specified position.

Here's the header for the .htc file, in which the slider tag is defined.

<public:component tagname=slider>
<public:property name=movelr />
<public:property name=moveud />
<public:property name=backgroundColor />
<public:property name=backgroundImage />
<public:property name=backgroundRepeat />
<public:property name=thumbColor />
<public:property name=thumbSize />
<public:property name=value id=propValue put="setValue" get="getValue" />
<public:attach event="oncontentready" onevent="initialize()" />
<public:defaults viewLinkContent />
<public:event name="onslide" id="eSlide" />

The first line—the public:component declaration—is needed because this is an element behavior.

Each of the properties is defined with a <public:property> tag, but only the value property defines a getter and a setter. This property needs a getter and a setter because the movement of the slider resets the property internally, and because it allows users to set it from the outside. The setter lets the behavior catch user changes to the value.

The declarative catching of the oncontentready event ensures proper timing of the initialization method. Since this tag isn't scoped, it's not as important; if this tag had internal content, use of the oncontentready notification would be critical. If you just use inline script to initialize, the tag may not be in a completely parsed state. The oncontentready event fires when all of the contents of the tag are parsed and available for access.

The viewLinkContent directive notifies the browser that the content for the rectangle defined by the <ie:slider> tag will be derived from the body of the associated .htc file. Let's take a look at the declaration of the content that will become the slider.

<DIV ID=theParent STYLE="background-color: scrollbar;
                         position: relative; width: 100%; height: 100%;">
  <DIV ID=theDiv STYLE="border: outset 2px;
                        background-color: scrollbar; cursor: hand;
                        position: absolute; top: 10px; left: 0px;
                        width: 15px; height: 50px;
                        z-index: 5; font-size: 1px">
  </DIV>
  <DIV ID=theMiddle STYLE="font-size:1px; background-color: black;
                           position: absolute;">
  </DIV>
</DIV>

The <DIV> "theParent" becomes the background for the slider. It's set up to be sized at 100 percent height and width, as this will completely fill the size specified by the user HTML page that contains the <ie:slider> tag. The fact that this <DIV> size is specified as a percentage leads to an interesting complication later on, as the size and position of the thumb are deduced.

The <DIV> "theDiv" becomes the thumb for the slider itself. It defaults to be the scrollbar color. Only some of the positioning properties are filled in, as the final values depend on whether the slider is working from left to right or from top to bottom. The remaining values for positioning are filled in during the initialization method.

The <DIV> "theMiddle" is the middle scale indicator. As with the thumb, the positioning properties are set mostly in the initialization method.

Slidin' Along

To implement dragging properly, follow these steps:

  1. Cancel selection for the element by using the onselectstart="return false" methodology. If you don't do this, the act of dragging will be interpreted as either text selection, image selection, or a real OLE drag-and-drop operation, which isn't what you want.
  2. For the onmousedown event on the targeted element, capture the mouse. Then go into a drag mode, in which you trap the onmousemove and onmouseup events.
  3. For any onmousemove event, position the dragged element appropriately. The best way to determine how far to move the object is to "remember" the mouse coordinates from the last drag, and calculate a delta based on the current mouse position. This approach results in the least jerkiness.
  4. When an onmouseup event is finally received, discontinue the drag, unhook the onmousemove and onmouseup events, and cancel the mouse capture—effectively ending the drag mode.

As each onmousemove event is processed, the slider throws the onslide event, with a special value on the event object:

  if (!noResetX && !noResetY) {
   event = createEventObject();
   event.sliderValue = dragPosition;
   realValue = dragPosition;
   eSlide.fire(event);
  }

This code is in the doDrag() method, which is set as the event handler for the onmousemove event.

The if statement in the code above guards against "overdragging"—dragging out of the range. The code detects this case, and doesn't throw multiple onslide events with the same repeating value.

Because the behavior adds a new property, sliderValue, to the event object, a custom event object needs to be created. Once the event object is set, simply call the fire method, with the new event object as a parameter. The fire method is called on the event object as declared in the header.

Calculating the Thumb

I mentioned above the complexity that results when the slider background size is defined as a percentage. This means that the way most people position elements—by looking at the style.posLeft and style.posTop properties—isn't quite going to work. These properties will return the values in the specified units, not in pixels. However, you can always access the pixel values of left/top/width/height by using the style.pixelLeft, style.pixelTop, style.pixelWidth, and style.pixelHeight properties. Here's a place in the code that does just that.

  if (dragElement.style.posTop + dragElement.style.posHeight >
      dragParent.style.pixelHeight) {
      dragElement.style.posTop = dragParent.style.pixelHeight –            dragElement.style.posHeight - 1 ;
      noResetY = true;
  }

This code detects whether the thumb has moved too far. The dragElement property represents the thumb. The dragParent property is the slider background. Note that dragElement's posTop and posHeight properties, which are specified in pixels, are compared to dragParent's pixelHeight property, which is in pixels, by definition.

Changing the Right Way

Note that if you have allowed the property setter function to change value, you need to call the fireChange() method on the property to ensure proper functioning of the onpropertychange event in the parent document. Careful coding will ensure consistency between your user-defined properties and the built-in properties of the system.

function setValue(newValue) {
  if ((newValue < 0) || (newValue > 100)) {
    return;
  }

  if (element.movelr) {
    ppV = dragParent.style.pixelWidth / 100;
    dragElement.style.posLeft = ppV * newValue;
  }

  if (element.moveud) {
    ppV = dragParent.style.pixelHeight / 100;
    dragElement.style.posTop = ppV * (100 - newValue);
  }

  realValue = newValue;
  propValue.fireChange();
}

Slidin' Away

So there you have it: a fully functional slider control, all in DHTML. It probably took me a lot less time than it would take to build a Win32 version, and I never needed to compile anything.

 

DHTML Dude

Michael Wallent is Microsoft's product unit manager for Internet Explorer.


  
Show: