How To: Prototype Resize Handle

This is just a short post to share some JavaScript that I wrote to be able to add a drag corner to an html element to make it resizable.

I did this using prototype.js. As an example the two divs below have been made resizable. (Go ahead. Drag the bottom right corner around.)

Div One
 

Div Two
 

And below is the code that makes it possible. If you take a look at it and read the comments, You'll see that it's actually pretty simple. The thing that had me stuck for a while was asking Prototype how tall or wide something is isn't the whole story. Border and padding add to width and height. I have some code in there that will compensate for that. However, I only covered situations where the border/passing were uniform all the way around. Anyway, love to hear what you think about this.

<div id="SomeDiv" style="position: relative; width: 200px; height:100px; border: 2px solid black; padding: 3px;">
   Div One
   <div class="corner" id="DragHandle">&nbsp;</div>
</div>

<div id="DivTwo" style="position:relative; width:150px; height:75px; border:1px dashed red;">
   Div Two
   <div class="corner" id="DragHandleTwo">&nbsp;</div>
</div>

<script type="text/javascript" language="javascript">

   function DragCorner(container, handle) {
      var container = $(container);
      var handle = $(handle);
      
      /* Add property to container to store position variables */
      container.moveposition = {x:0, y:0};
      
      function moveListener(event) {
         /* Calculate how far the mouse moved */
         var moved = {
                     x:(event.pointerX() - container.moveposition.x),
                     y:(event.pointerY() - container.moveposition.y)
                  };
         /* Reset container's x/y utility property */
         container.moveposition = {x:event.pointerX(), y:event.pointerY()};
         /* Border adds to dimensions */
         var borderStyle = container.getStyle('border-width');
         var borderSize = borderStyle.split(' ')[0].replace(/[^0-9]/g,'');
         /* Padding adds to dimensions */
         var paddingStyle = container.getStyle('padding');
         var paddingSize = paddingStyle.split(' ')[0].replace(/[^0-9]/g,'');
         /* Add things up that change dimensions */
         var sizeAdjust = (borderSize*2) + (paddingSize*2);
         /* Update container's size */
         var size = container.getDimensions();
         container.setStyle({
               height: size.height+moved.y-sizeAdjust+'px',
               width:size.width+moved.x-sizeAdjust+'px'
            });
      }
      
      /* Listen for 'mouse down' on handle to start the move listener */
      handle.observe('mousedown', function(event) {
         /* Set starting x/y */
         container.moveposition = {x:event.pointerX(),y:event.pointerY()};
         /* Start listening for mouse move on body */
         Event.observe(document.body,'mousemove',moveListener);
      });
      
      /* Listen for 'mouse up' to cancel 'move' listener */
      Event.observe(document.body,'mouseup', function(event) {
         Event.stopObserving(document.body,'mousemove',moveListener);
      });
   }
   
   DragCorner('SomeDiv','DragHandle');
   
   DragCorner('DivTwo','DragHandleTwo');
   
</script>

Comments
Jamie Krug's Gravatar @Chris, looks great. Did you also check out the <a href="http://docs.jquery.com/UI/API/1.7/Resizable">jQuery UI's Resizable</a>? You've gotta like doing it with one line of JavaScript :)

$("any_selector").resizable();

There are tons of other customizations available, but the basics are nice and simple. Just thought I'd throw it out there.
# Posted By Jamie Krug | 3/2/09 7:19 AM
Chris Phillips's Gravatar @Jamie, that jQuery method works really slick. I bet they already did a way better job than I ever will with all the math to make it work correctly with different properties in different browsers. But, we use, prototype and scriptaculous at work. And I didn't see a "Resizable" method for those. So, I just did it really quick for a fun experiment.
# Posted By Chris Phillips | 3/2/09 10:45 AM
Lemmi's Gravatar Great Idea. Why don't you create in a class like for example scriptaculous? Although great work!
# Posted By Lemmi | 7/14/09 4:29 AM
Nazmul's Gravatar If the div is surrounded by Iframe, it is difficult to rezise. While user drag to resize and the mouse up event occurs in the iframe, the resize handler does not stop its function. How can I get rid of it?

Thank you so much.
# Posted By Nazmul | 5/23/10 10:33 AM
Wagner Montalvao Camarao's Gravatar Hey Chris,

Great job! Just a note, I tried to use this, having the css definitions from an external stylesheet instead of inline, and it didn't work. Prototype couldn't get the border-width and padding values with getStyle(). To solve that, I used all the most specific attribute names: border-left-width, border-top-width, etc. and then I calculated the average of the 4 borders. Here is the changed piece of code:


var borderTopWidth = container.getStyle('border-top-width');
var borderBottomWidth = container.getStyle('border-bottom-width');
var borderLeftWidth = container.getStyle('border-left-width');
var borderRightWidth = container.getStyle('border-right-width');

var borderStyleAverage = (borderTopWidth + borderBottomWidth + borderLeftWidth + borderRightWidth) / 4;
var borderSize = borderStyleAverage.split(' ')[0].replace(/[^0-9]/g,'');


It seems to be a prototype issue, because it works for inline css and not for external stylesheets, although I saw some guys saying that it's a DOM issue, since prototype docs say "Not all CSS shorthand properties are supported. You may only use the CSS properties described in the Document Object Model (DOM) Level 2 Style Specification." By the way, we can do the same for the padding, using padding-left, padding-right, etc.
# Posted By Wagner Montalvao Camarao | 3/10/11 10:35 AM
Wagner Montalvao Camarao's Gravatar Sorry, there's a mistake in my code above. Here is it fixed, everything together now:


function DragCorner(container, handle) {
   
   var container = $(container);
   var handle = $(handle);
   
   container.moveposition = { x: 0, y: 0 };
   
   function moveListener(event) {
      
      var moved = {
         x: (event.pointerX() - container.moveposition.x),
         y: (event.pointerY() - container.moveposition.y)
      };
      
      container.moveposition = { x: event.pointerX(), y: event.pointerY() };
      
      function extractNumber(text) {
         return +text.split(' ')[0].replace(/[^0-9]/g,'');
      }
      
      var borderTop = extractNumber(container.getStyle('border-top-width'));
      var borderBottom = extractNumber(container.getStyle('border-bottom-width'));
      var borderLeft = extractNumber(container.getStyle('border-left-width'));
      var borderRight = extractNumber(container.getStyle('border-right-width'));
      
      var paddingTop = extractNumber(container.getStyle('padding-top'));
      var paddingBottom = extractNumber(container.getStyle('padding-bottom'));
      var paddingLeft = extractNumber(container.getStyle('padding-left'));
      var paddingRight = extractNumber(container.getStyle('padding-right'));
      
      var heightAdjust = borderTop + borderBottom + paddingTop + paddingBottom;
      var widthAdjust = borderLeft + borderRight + paddingLeft + paddingRight;
      
      var size = container.getDimensions();
      
      container.setStyle({
         height: size.height + moved.y - heightAdjust + 'px',
         width: size.width + moved.x - widthAdjust + 'px'
      });
   }
   
   handle.observe('mousedown', function(event) {
      container.moveposition = {x:event.pointerX(),y:event.pointerY()};
      Event.observe(document.body,'mousemove',moveListener);
   });
   
   Event.observe(document.body,'mouseup', function(event) {
      Event.stopObserving(document.body,'mousemove',moveListener);
   });
}

document.observe('dom:loaded', function() {
   DragCorner('dragBox', 'dragBoxCorner');
});
# Posted By Wagner Montalvao Camarao | 3/10/11 11:49 AM
Chris Phillips's Gravatar Wagner,

Thanks for the update on this. When I actually went to use it at work, I ran in the the exact issue you are describing. I ended up cheating and creating a version where you told it how much it need to compensate on with/height. I will look at updating it to use this code.

Happy Coding,

cfchris
# Posted By Chris Phillips | 3/11/11 11:10 AM
NFL Pittsburgh Steelers's Gravatar Pittsburgh Steeler Jerseyalso lies in the fact that they are fashionable and the youngsters love to wear them casually for the comfort and confidence they gain from the Cheap Steeler Jerseys.
# Posted By NFL Pittsburgh Steelers | 1/15/12 7:04 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.6.002.