Making elements drag resizable with Javascript
Drag resizing is a great interface interaction for web apps using panes, or window regions. Most Javascript libraries have convenient and simple methods to make drag-resizing a snap. However, what if you didn’t have a library at your disposal or were just interested in how to implement this interaction, just for the sake of knowing? Well I did just that when I implemented drag resizable panels for DebugKit 1.2 (coming soon to a tar file near you). The following examples will use the DebugKit Javascript tools. While this post will focus on vertical resizing, the same principles would apply for horizontal or vertical+horizontal resizing.
Components of drag resizing
Drag-resizing is made up of 3 separate events and usually 2 DOM nodes. You can do it with one DOM node, but I used two. The three events in used are mousedown
, mousemove
and mouseup
, and they need to be handled in that order. The two DOM nodes I used were the panel being resized, and then handle being held. If you are using only one element your implementation will differ a bit.
Mousedown event
The mousedown
event signals the beginning of the resizing, and is bound to the handle element. In this mousedown
handler we want to do a few things. First we want to bind the mouseup
and mousemove
event handlers as those are only required after the mousedown
event has occurred. Secondly, we want to store the initial mouse position, and original object height. The initial mouse position is useful as it allows us to determine how far the mouse has moved vertically. The initial height is used to calculate the new height. Original height + mouse distance traveled = new height.
So with that all in mind my mousedown
event looks like this:
- Event.addEvent(element, 'mousedown', function (event) {
- event.preventDefault();
- currentElement = this;
- this._startY = event.pageY;
- this._startHeight = parseInt(Element.height(Element.getPrevious(currentElement)));
- // attach to document so mouse doesn't have to stay precisely on the 'handle'
- Event.addEvent(document, 'mousemove', mouseMoveHandler);
- Event.addEvent(document, 'mouseup', mouseUpHandler);
- });
A few things to note, I didn’t make currentElement
a local variable. This is intentional, since the DebugKit tools don’t provide scope modification or event augmentation tools, I use a closure to maintain a reference to the active element. Since my handle and panel elements are siblings I use Element.height(Element.getPrevious(currentElement))
to get the starting height of the panel element. Also in the above code you can see the storage of the previously mentioned properties, as well as the binding of the other events. The other events are bound to document
because binding them to the active element makes the interaction very fiddly and annoying to use. While binding events to the document object makes the interaction smooth and error free.
Mousemove event
Now mouseMoveHandler
is a function that looks like:
- var mouseMoveHandler = function (event) {
- event.preventDefault();
- var newHeight = currentElement._startHeight + (event.pageY - currentElement._startY);
- Element.height(Element.getPrevious(currentElement), newHeight);
- }
This event handler takes the current mouse position event.pageY
and finds the difference from currentElement._startY
Where currentElement.startY
is the panel element’s original height. The result is assigned to the panel element as its new height. mousemove
events are fired each time the mouse is moved, so the result of the above method is a smooth resizing of the panel as long as the mouse is held down
Mouseup event
Mouseup is fired when the user releases the mouse button indicating completion of drag-resizing. As seen above my handler was called mouseUpHandler
, it looks like:
- var mouseUpHandler = function (event) {
- currentElement = null;
- Event.removeEvent(document, 'mousemove', mouseMoveHandler);
- Event.removeEvent(document, 'mouseup', mouseUpHandler);
- }
This event handler nulls currentElement
as the mouse has been released and there is no longer an active element. It also unbinds the mousemove
and mouseup
events. Now when you are unbinding events with basic DOM methods you need the original Function object in order to unbind the events. This is another reason why these events are not anonymous functions being passed into the addEvent
method, doing so would render them impossible to unbind.
So that’s basically it, drag-resizing is actually fairly straightforward once you get which events to use, and how to combine them together. For reference here is the whole makePanelDraggable
function from the DebugKit javascript.
- makePanelDraggable: function (panel) {
- var currentElement = null;
- var mouseMoveHandler = function (event) {
- event.preventDefault();
- var newHeight = currentElement._startHeight + (event.pageY - currentElement._startY);
- Element.height(Element.getPrevious(currentElement), newHeight);
- }
- var mouseUpHandler = function (event) {
- currentElement = null;
- Event.removeEvent(document, 'mousemove', mouseMoveHandler);
- Event.removeEvent(document, 'mouseup', mouseUpHandler);
- }
- for (var i in panel.content.childNodes) {
- var element = panel.content.childNodes[i],
- tag = element.nodeName ? element.nodeName.toUpperCase() : false;
- if (tag === 'DIV' && Element.hasClass(element, 'panel-resize-handle')) {
- Event.addEvent(element, 'mousedown', function (event) {
- event.preventDefault();
- currentElement = this;
- this._startY = event.pageY;
- this._startHeight = parseInt(Element.height(Element.getPrevious(currentElement)));
- Event.addEvent(document, 'mousemove', mouseMoveHandler);
- Event.addEvent(document, 'mouseup', mouseUpHandler);
- });
- }
- }
- }
If you are more interested in how the DebugKit Javascript tools work, there is no reference documentation. But in the past I posted some documentation and there are always the comments in the source
Plausible, bot not confirmed.
A demo would make all the difference!
Tore T on 9/1/10
Well the actual code I used in this article became part of Debug Kit so that’s kind of a demo I guess.
mark story on 9/1/10
looks good, but i have no idea how i would incorporate that into my code to make it do something…
j on 3/25/11
j: The main use would be if you needed resizable interface elements, such as dialog boxes or panels.
mark story on 4/2/11
Look pretty, but how to use it ?
A small example of implementation inside a minimalist html page would be very welcome.
Thanks
Pascal on 10/19/11
dude, show me your demos
Kamal Reddy on 3/20/13
WOW! you’ve made this very simple! thank you so much! used you in an app im building! definitely sending a shout out to you in it… will send you a link to it when im done.
years on and this site has rocked my world.
Craig Wayne on 2/13/14