Dragger //JavaScript Repository

Description

Allows dragging objects as well as applying movimentation filters.
Created: 2005.08.08 - Modified 2005.11.30

Dependencies

Code (Download)

//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com/dhtml/drag-library [rev. #2]

Dragger = function(o, a){
    var $ = this;
    o.style.position = "absolute", $.object = o, $.d = {x: 0, y: 0}, $.f = [];
    a && (addEvent(o, "mousedown", function(){return this.start(), false;}, $),
        addEvent(document, "mouseup", function(){this.dragging && this.stop();}, $));
}
with({p: Dragger.prototype, c: Dragger}){
    p._updateMouse = function(e){
        var w = window, b = document.body;
        p.mouse = {x: e.clientX + (w.scrollX || b.scrollLeft || b.parentNode.scrollLeft || 0),
            y: e.clientY + (w.scrollY || b.scrollTop || b.parentNode.scrollTop || 0)};
    };
    addEvent(document, "mousemove", p._updateMouse);
    p.mouse = {x: 0, y: 0};
    p.dragging = false;
    p.start = function(center){
        var r, $ = this, m = $.mouse, o = $.object;
        for(var r = {l: o.offsetLeft, t: o.offsetTop, w: o.offsetWidth, h: o.offsetHeight};
            o = o.offsetParent; r.l += o.offsetLeft, r.t += o.offsetTop);
        !$.dragging && ($.dragging = true, o = $.object, $.d = center &&
            (m.x < r.l || m.x > r.l + r.w || m.y < r.t || m.y > r.t + r.h) ?
            {x: r.w / 2, y: r.h / 2} : {x: m.x - o.offsetLeft, y: m.y - o.offsetTop},
            addEvent(document, "mousemove", $.drag, $),
            this.callEvent("onstart"));
    };
    p.drag = function(e){
        var i, p, $ = this, o = $.object, m = ($._updateMouse(e), (m = $.mouse).x -= $.d.x, m.y -= $.d.y, m);
        for(i = $.f.length; i; $.f[--i] && $.f[i][0].apply(m, $.f[i][1]));
        o.style.left = m.x + "px", o.style.top = m.y + "px";
        return !!this.callEvent("ondrag", e);
    };
    p.stop = function(){
        this.dragging = false;
        removeEvent(document, "mousemove", this.drag, this);
        this.callEvent("onstop");
    };
    p.addFilter = function(f, arg0, arg1, arg2, argN){
        this.f[this.f.length] = [f, [].slice.call(arguments, 1)];
    };
    p.callEvent = function(e){
        return this[e] instanceof Function ? this[e].apply(this, [].slice.call(arguments, 1)) : undefined;
    };
}

Example (Example)

<style type="text/css">
.box{position: absolute; border: 1px solid #999; width: 50px; text-align: center; font: 12px monospace; padding: 5px 0px;}
#customized{top: 150px; width: 200px; height: 200px; background-color: #fef;}
#circle{background-color: #fee;}
#square{background-color: #ccc; left: 50px;}
#freeLine{background-color: #eff; left: 100px;}
#polyLine{background-color: #efe; left: 150px;}
#free{background-color: #eef; left: 200px;}
</style>

<div class="box" id="free">Free Drag</div>
<div class="box" id="polyLine">Poly Lines</div>
<div class="box" id="freeLine">Free Line</div>
<div class="box" id="square">Square Limit</div>
<div class="box" id="circle">Circle Limit</div>

<div id="customized" class="box">Hold the SHIFT key to start moving this box by the center, hold the CTRL to move it without centering. Leave the key to stop.</div>

<script type="text/javascript">
//<![CDATA[

//simple example
new Dragger(document.getElementById("free"), true);

//customized dragging
o = new Dragger(document.getElementById("customized"));
addEvent(document, "keydown", function(e){
    if(e.shiftKey || e.ctrlKey)
        o.start(e.shiftKey);
});
addEvent(document, "keyup", function(e){
    if(!e.shiftKey && !e.ctrlKey)
        o.stop();
});

//examples using filters
function lineLength(x, y, x0, y0){
    return Math.sqrt((x -= x0) * x + (y -= y0) * y);
}
SQUARE = function(x, y, w, h){
    this.x = this.x < x ? x : this.x > x + w ? x + w : this.x,
    this.y = this.y < y ? y : this.y > y + h ? y + h : this.y;
};
new Dragger(document.getElementById("square"), true).addFilter(SQUARE, 200, 200, 400, 200);

CIRCLE = function(x, y, ray){
    var tg;
    lineLength(this.x, this.y, x += ray, y += ray) > ray && (this.x = Math.cos(tg = Math.atan2(this.y - y, this.x - x)) * ray + x, this.y = Math.sin(tg) * ray + y);
};
new Dragger(document.getElementById("circle"), true).addFilter(CIRCLE, 100, 100, 100);

LINE = function(x, y, angle){
    if(!(angle % 90))
        return this.x = x;
    var tg = Math.tan(-angle * Math.PI / 180);
    Math.sin(45 * Math.PI / 180) >= Math.sin(angle * Math.PI / 180) ? this.y = (this.x - x) * tg + y : this.x = (this.y - y) / tg + x;
};
new Dragger(document.getElementById("freeLine"), true).addFilter(LINE, 200, 400, 60);

POLY = function(x0, y0, x1, y1, etc, etc, etc){
    function dotLineLength(x, y, x0, y0, x1, y1, o){
        if(o && !(o = function(x, y, x0, y0, x1, y1){
            if(!(x1 - x0)) return {x: x0, y: y};
            else if(!(y1 - y0)) return {x: x, y: y0};
            var left, tg = -1 / ((y1 - y0) / (x1 - x0));
            return {x: left = (x1 * (x * tg - y + y0) + x0 * (x * - tg + y - y1)) / (tg * (x1 - x0) + y0 - y1), y: tg * left - tg * x + y};
        }(x, y, x0, y0, x1, y1), o.x >= Math.min(x0, x1) && o.x <= Math.max(x0, x1) && o.y >= Math.min(y0, y1) && o.y <= Math.max(y0, y1))){
            var l1 = lineLength(x, y, x0, y0), l2 = lineLength(x, y, x1, y1);
            return l1 > l2 ? l2 : l1;
        }
        else{
            var a = y0 - y1, b = x1 - x0, c = x0 * y1 - y0 * x1;
            return Math.abs(a * x + b * y + c) / Math.sqrt(a * a + b * b);
        }
    }
    for(var a = [].slice.call(arguments, 0), lines = []; a.length > 3; lines[lines.length] = {y1: a.pop(), x1: a.pop(), y0: a.pop(), x0: a.pop()});
    if(!lines.length)
        return;
    for(var l, i = lines.length - 1, o = lines[i], lower = {i: i, l: dotLineLength(this.x,    this.y, o.x0, o.y0, o.x1, o.y1, 1)}; i--; lower.l > (l = dotLineLength(this.x, this.y, (o = lines[i]).x0, o.y0, o.x1, o.y1, 1)) && (lower = {i: i, l: l}));
    this.y < Math.min((o = lines[lower.i]).y0, o.y1) ? this.y = Math.min(o.y0, o.y1) : this.y > Math.max(o.y0, o.y1) && (this.y = Math.max(o.y0, o.y1));
    this.x < Math.min(o.x0, o.x1) ? this.x = Math.min(o.x0, o.x1) : this.x > Math.max(o.x0, o.x1) && (this.x = Math.max(o.x0, o.x1));
    Math.abs(o.x0 - o.x1) < Math.abs(o.y0 - o.y1) ? this.x = (this.y * (o.x0 - o.x1) - o.x0 * o.y1 + o.y0 * o.x1) / (o.y0 - o.y1) : this.y = (this.x * (o.y0 - o.y1) - o.y0 * o.x1 + o.x0 * o.y1) / (o.x0 - o.x1);
};
b = new Dragger(document.getElementById("polyLine"), true);
b.addFilter(POLY, 0, 0, 200, 200, 200, 200, 400, 200, 400, 200, 600, 0);

//]]>
</script>

Help

Constructor

Dragger(element: HTMLElement, [autoBind: Boolean = false])
Generates an instance of Dragger.
element
element that will receive the hability to be dragged
autoBind
if true, adds the default drag behaviour to the element, starting up by pressing a mouse button and finishing when it gets released

Methods

Dragger.start([center: Boolean = true]): void
Starts dragging the element.
center
if true, the object will be centralized in the current position of the mouse if the mouse isn't already over the element
Dragger.stop(): void
Stops dragging the element.
Dragger.addFilter(filter: Function(): void, [arg0, arg1, arg2, argN, ...]): void
Adds a movimentation filter to the object, allowing additional arguments to be sent to the filter.
filter
this function will be called whenever the mouse is moved, its scope (this), contains two properties, "x" and "y", by changing any of them you change the current object position
arg0, arg1, arg2, argN, ...
the specified arguments after the filter will be sent as arguments to the own filter, in the same order they were specified

Properties

Dragger.object: HTMLElement
object linked within the Dragger
Dragger.dragging: Boolean
tells if the object is beeing dragged
Dragger.mouse: Object
keeps the current mouse position, it's composed by two properties, x and y

Events

Dragger.onstart: function(void): void
Occurs when the drag starts.
Dragger.ondrag: function(event: Event): void
Occurs while the object is beeing dragged.
event
event object
Dragger.onstop: function(void): void
Occurs when the drag finishes.

Rank (Votes: 52)

4.52