+
+
+
+
Tether
+
Tether is a JavaScript library for efficiently making an absolutely positioned
+element stay next to another element on the page. For example, you might
+want a tooltip or dialog to open, and remain, next to the relevant item
+on the page.
+
Tether includes the ability to constrain the element within the viewport, its
+scroll parent, any other element on the page, or a fixed bounding box. When it
+exceeds those constraints it can be pinned to the edge, flip to the other
+side of its target, or hide itself.
+
Tether optimizes its location placement to result in the minimum amount of
+'jankyness' as the page is scrolled and resized. The page can maintain 60fps
+scrolling even with dozens or hundreds of tethers on screen (pop open the
+devtools timeline as you scroll this page).
+
Tether is 5kb minified and gzipped, and supports IE 10+, and all modern
+browsers.
+
+
+Select
+Drop
+Tooltip
+Shepherd
+
+
+
Usage
+
The element to be moved is called the 'element'.
+The element in the page it's to be attached to is called the 'target'.
+
To use Tether, you define a point on the target and a point on the element.
+Tether moves the element to keep those two points on top of each other.
+
That point is called the attachment (we've marked it in the examples with
+a red ). For example, if you'd like
+the element to sit on the left of the target:
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'top right',
+ targetAttachment: 'top left'
+});
+
+
Attachment
+
You can move the attachment points of both the element and the target.
+
For example, lets move the element's attachment:
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'bottom left' ,
+ targetAttachment: 'top left'
+});
+
+
We can also change the target's attachment point:
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'bottom left',
+ targetAttachment: 'bottom right'
+});
+
+
There are two more attachment points we haven't seen yet, center and middle:
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'middle center' ,
+ targetAttachment: 'middle center'
+});
+
+
All told, Tether provides six built in attachment positions:
+
+left
+center
+right
+top
+middle
+bottom
+
+
The syntax of the attachment properties is: "vertical-attachment horizontal-attachment".
+
You must always supply an attachment. If you don't supply a target-attachment, it is
+assumed to be the mirror image of attachment.
+
Offset
+
The six attachment points we provide are not always enough to place the element
+exactly where you want it. To correct this, we provide two more properties,
+offset and targetOffset.
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'top right',
+ targetAttachment: 'top left',
+ offset: '0 10px'
+});
+
+
As you can see, we've moved the attachment point of the element 10px to the right.
+We can also move the attachment point of the target:
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'top right',
+ targetAttachment: 'top left',
+ offset: '0 10px',
+ targetOffset: '20px 0'
+});
+
+
The offset properties also accept percentages. Percentages in offset refer to
+the height and width of the element, targetOffset the height and width of
+the target.
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'top right',
+ targetAttachment: 'top left',
+ targetOffset: '0 75%'
+});
+
+
The syntax of the offset properties is "vertical-offset horizontal-offset"
+
Tether offers a couple of special attachments, using the targetModifier
+option:
+
new Tether({
+ element: yellowBox,
+ target: scrollBox,
+ attachment: 'middle right',
+ targetAttachment: 'middle left',
+ targetModifier: 'scroll-handle'
+});
+
+
Set the target to document.body to have the element follow the page's scroll bar.
+
The targetModifier visible can be used to attach an element to the visible part
+of an element:
+
new Tether({
+ element: yellowBox,
+ target: document.body,
+ attachment: 'middle center',
+ targetAttachment: 'middle center',
+ targetModifier: 'visible'
+});
+
+
new Tether({
+ element: yellowBox,
+ target: scrollBox ,
+ attachment: 'middle center',
+ targetAttachment: 'middle center',
+ targetModifier: 'visible'
+});
+
+
Constraints
+
If you have tried any of the previous examples, you'll notice that it's pretty
+easy to scroll the regions in such a way that the element is hanging out on
+its own, with no target in sight.
+
Constraints allow you to control what happens when the tethered element would
+have to fall outside of a defined region to maintain the attachment.
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'middle left',
+ targetAttachment: 'middle left',
+ constraints : [
+ {
+ to: 'scrollParent',
+ pin: true
+ }
+ ]
+});
+
+
We've created a constraint which will keep the element within its scroll
+parent by 'pinning' it to the edges if it tries to escape. For the sake
+of the example, we're also highlighting the pinned edge in red.
+
Specify an array of sides if you'd only like to pin those edges:
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'middle left',
+ targetAttachment: 'middle left',
+ constraints: [
+ {
+ to: 'scrollParent',
+ pin: ['top']
+ }
+ ]
+});
+
+
You might want to allow the element to change its attachment, if doing so
+would keep more of it within its assigned region:
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'top left',
+ targetAttachment: 'bottom left',
+ constraints: [
+ {
+ to: 'scrollParent',
+ attachment: 'together'
+ }
+ ]
+});
+
+
If you scroll the example a bit, you'll see it flip the attachment when necessary.
+You can combine pin and attachment as well:
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'top left',
+ targetAttachment: 'bottom left',
+ constraints: [
+ {
+ to: 'scrollParent',
+ attachment: 'together',
+ pin: true
+ }
+ ]
+});
+
+
Attachment will accept any of these values:
+
+element: Only change the element's attachment
+target: Only change the target's attachment
+both: Change either's attachment (or both), as needed
+together: Change both the element's and target's attachment at the same time (to
+'flip' the element to the other side of the attachment)
+none: Don't allow changes to attachment (the default)
+
+
Together is the option you will use most commonly:
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'top right',
+ targetAttachment: 'bottom left',
+ constraints: [
+ {
+ to: 'scrollParent',
+ attachment: 'together'
+ }
+ ]
+});
+
+
You can also provide different settings for the vertical and horizontal attachments:
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'top left',
+ targetAttachment: 'bottom left',
+ constraints: [
+ {
+ to: 'scrollParent',
+ attachment: 'together none'
+ }
+ ]
+});
+
+
Whenever the element is out of the constrained area, we add the tether-out-of-bounds
+class to it. If you add some CSS to make items with that class display: none, the
+tether will hide.
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'middle center',
+ targetAttachment: 'middle center',
+ constraints: [
+ {
+ to: 'scrollParent'
+ }
+ ]
+});
+
+
You can also constrain the element to the viewport, you'll have to scroll the
+page to see this one.
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'top left',
+ targetAttachment: 'bottom left',
+ constraints: [
+ {
+ to: 'window' ,
+ attachment: 'together'
+ }
+ ]
+});
+
+
You can, of course, use pin with the window as well to
+make it always visible no matter where the user scrolls:
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'top left',
+ targetAttachment: 'bottom left',
+ constraints: [
+ {
+ to: 'window',
+ attachment: 'together',
+ pin: true
+ }
+ ]
+});
+
+
to can be any of:
+
+'scrollParent'
+'window'
+any DOM element
+an array of bound points relative to the body [X1, Y1, X2, Y2]
+
+
You can also provide multiple constraints, keeping in mind that they are
+processed in the order supplied (the last one always has the final word).
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'top left',
+ targetAttachment: 'bottom left',
+ constraints: [
+ {
+ to: 'scrollParent' ,
+ pin: true
+ },
+ {
+ to: 'window' ,
+ attachment: 'together'
+ }
+ ]
+});
+
+
Optimization
+
Element Moving
+
The goal of Tether's optimizer is to not have to change the positioning
+CSS as the page is scrolled or resized. To accomplish this it looks at the
+last few positions, finds commonalities, and uses them to decide whether to
+position the element absolutely or with fixed positioning.
+
If the element is fully contained within its scroll parent, its DOM node
+can also be moved inside the scroll parent, to avoid repaints as the
+container is scrolled.
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'top left',
+ targetAttachment: 'bottom left'
+});
+
+
We are moving where the DOM node is, so if you have CSS which styles elements
+within the offset parent, you may see some rendering changes. Also note
+that this optimization works best if the scroll parent is the offset parent.
+In other words, the scroll parent should be made position relative, fixed or
+absolute to enable this optimization.
+
If you do see stylistic changes occur when the element is moved,
+you might want to disable this optimization. You can do that by
+setting optimizations.moveElement to false.
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'top left',
+ targetAttachment: 'bottom left',
+ optimizations: {
+ moveElement: false
+ }
+});
+
+
GPU
+
By default tether positions elements using CSS transforms. These transforms allow the
+tethered element to be moved as its own layer to not force a repaint of the underlying
+page.
+
This method of positioning can cause some issues however, including color shifts and artifacts.
+
If you experience these issues, you can disable this optimization by setting optimizations.gpu
+to false:
+
new Tether({
+ element: yellowBox,
+ target: greenBox,
+ attachment: 'top left',
+ optimizations: {
+ gpu: false
+ }
+});
+
+
Methods
+
The Tether constructor we've been using in these examples returns us a
+Tether object.
+
The Tether object has these methods:
+
+setOptions({ options }) - Update any of the options (such as attachment)
+disable() - Disable the tethering
+enable() - Enable the tethering
+destroy() - Disable and remove all references
+position() - Manually trigger a repositioning
+
+
Events
+
The Tether object also has events support, since it extends our Evented class.
+
The methods exposed to listen to events are:
+
+on(event, handler) - Adds an event listener that is fired whenever the event is fired
+once(event, handler) - Adds an event listener that is only fired the first time the event is fired
+off(event, handler) - Removes the event listener
+trigger(event) - Manually triggers events
+
+
The events fired are:
+
+repositioned - Fired whenever the tether element is moved
+update - Fired whenever the Tether instance runs into a constraint. You could use this
+for things like manually flipping an arrow or other tweaks.
+
+
Options
+
The full list of options which can be passed to the Tether constructor and
+setOptions:
+
+element: The DOM element, jQuery element, or a selector string of an element which will be moved
+target: The DOM element, jQuery element, or a selector string of an element which the element will be attached to
+attachment: A string of the form 'vert-attachment horiz-attachment'
+vert-attachment can be any of 'top', 'middle', 'bottom'
+horiz-attachment can be any of 'left', 'center', 'right'
+
+
+targetAttachment: A string similar to attachment.
+The one difference is that, if it's not provided, targetAttachment will assume the mirror
+image of attachment.
+offset: A string of the form 'vert-offset horiz-offset'
+vert-offset and horiz-offset can be of the form "20px" or "55%"
+
+
+targetOffset: A string similar to offset, but refering to the offset of the target
+targetModifier: Can be set to 'visible' or 'scroll-handle'
+enabled: Should the tether be enabled initially? Defaults to true.
+classes: A hash of classes which should be changed or disabled
+classPrefix: The prefix placed at the beginning of the default classes, defaults to 'tether'
+optimizations: A hash of optimizations, used to disable them
+constraints: An array of constraint definition objects. Each definition is of the form:
+to: A DOM element, bounding box, the string 'window', or the string 'scrollParent'
+pin: true or an array of strings representing the sides of the constraint
+attachment: A string of the form "vert-modifier horiz-modifier", or a single value
+representing both
+Each modifier should be one of "none", "together", "element", "target", or "both".
+
+
+outOfBoundsClass: An alternative to "tether-out-of-bounds", useful if the class
+needs to be differentiated from that of another constraint.
+pinnedClass: An alternative to "tether-pinned", similar to outOfBoundsClass.
+
+
+
+
Classes
+
Tether adds a variety of classes to the element and target to allow you to style
+them based on their tethering.
+
You can change the prefix of the classes with the classPrefix option. It is 'tether' by
+default, but you could, for example, change it to be 'bill' if you were building the bill
+library and all the classes would be 'bill-*'.
+
new Tether({
+ classPrefix: 'bill'
+});
+
+
The sass/css is similarily configurable, see
+tooltip for
+an example of how to make your own prefixed css file.
+
All classes can be changed or disabled with the classes option. For example, to change the
+tether-element class to be my-box:
+
new Tether({
+ classes: {
+ element: 'my-box'
+ }
+});
+
+
You can also disable classes you're not going to use:
+
new Tether({
+ classes: {
+ out-of -bounds: false
+ }
+});
+
+
+tether-element is added to the element
+tether-target is added to the target
+tether-enabled is added to both elements when tether is not disabled
+tether-element-attached-[left,right,top,bottom,middle,center] is added to both
+elements based on the elements attachment, if the element becomes detached (for
+example, if it's pinned), that class is removed. The class reflects how the
+element is actually attached, so if a constraint changes the attachment, that
+change will be reflected in the class.
+tether-target-attached-[left,right,top,bottom,middle,center] is added to both
+elements based on the target's attachment. All of the characteristics are the
+same as for element-attached.
+
+
+
+tether-out-of-bounds, tether-out-of-bounds-[side] are added to both the element and the target
+when the element is placed outside of its constraint.
+tether-pinned, tether-pinned-[side] are added to both the element and target when a constraint
+has pinned the element to the [side] of the container.
+
+
Browser Support
+
Tether supports IE9+, and all modern browsers.
+
Google doesn't support IE8, Microsoft is dropping support in a few months, and not supporting it saves
+us a whole lot of trouble. If you are interested in adding support, get in touch, we're happy to accept
+a PR.
+
Contributing
+
Please contribute! Tether is developed in Coffeescript, but if that's problematic for you, feel free
+to submit pull requests which just change the JavaScript files, we can adapt them as needed.
+
To build Tether, you need:
+
+
Instructions
+
+Install the build tool
+
+
npm install -g gulp
+
+
+
+npm install
+
+
+
gulp
+
+