Resizeable DIVs (aka SplitPanes)

I've been using ruby-on-rails and script.aculo.us for a portal I've been writing but I needed the ability to resize the columns in the portal. Dojo has something that could do it, but I've no idea how to make that play nice with script.aculo.us and, anyway, the point of doing this sort of thing for fun is to learn, so I set out to do it myself. Below shows the result. In this case there happen to be three columns but there could be two or four or whatever.
This looks pretty garish – I’ve styled this example using CSS so that you can see what is happening.:

This is some clipped text that will be in the left column.
This is some wrapped text that will be in the left column.

This is some text that will be in the middle column.

This is some text that will be in the right column.

 

The basic concept is that each column has to be a div which is a child of the same parent div. This is the HTML for the above example:

<div id="parent">
<div id="lhs" class="column"><p class="content">
<span style="display: block; white-space: nowrap; overflow: hidden">This is some clipped text that will be in the left column.</span>
<span style="display: block">This is some wrapped text that will be in the left column.</span>
</p></div>
<div id="mid" class="column"><p class="content">This is some text that will be in the middle column.</p></div>
<div id="rhs" class="column"><p class="content">This is some text that will be in the right column.</p></div>
</div>

Then, you just create some instances of the JavaScript SplitPane class as follows:

<script language="JavaScript" type="text/JavaScript"><!--
new SplitPane("lhs", 33.3, "mid", 33.33, 33.3, { active: true });
new SplitPane("mid", 33.3, "rhs", 66.66, 33.3, { active: true }); //-->
</script>

This class creates another div with the class splitpane-divider and positions it half-way between the two columns. If you specify the active option it then wires it all up to events so that you can drag the divider around and the columns are resized as you do so.

Why is it optional to allow you to actually resize the columns? Well in my application, if the owner of a page is viewing the page then they can modify its appearance. If anyone else is viewing the page then they can't. So depending on who is viewing the page the splitpane is made active or it isn't.

The SplitPane class has a serialize method which can be used to post the changes to the column widths and positions to a server using AJAX – this is basically the same type of functionality that the script.aculo.us sortable class provides – it allows you to save changes to columns in a persistent store. This wouldn't be much use unless you could tell when a column was being resized, so you can pass in optional event handlerswhen you create an instance of a splitpane. Here's an example of some code that will do that:

<script language="JavaScript" type="text/JavaScript"><!--
function handleDragEnd(splitPane, event) {

    new Ajax.Request('/main/split', {asynchronous:true, evalScripts:true, parameters:splitPane.serialize()});
}
new SplitPane("lhs", 33.33, "mid", 33.33, 33.33, { onEnd: handleDragEnd, active: true });
new SplitPane("mid", 33.33, "rhs", 66.66, 33.33, { onEnd: handleDragEnd, active: true }); //-->
</script>

The exact result you obtain depends to some extent on the CSS you use – in particular the column CSS. This is the CSS I used in the above example:

/* Elements with this class are generated by the JavaScript code */
/* Make the dividers visible for this demo */
.splitpane-divider {
    border: 1px solid black;
    background:#5B207B;
    opacity: .5;
    filter: alpha(opacity=50);
}

/* The classes/ids below are just used in this example. You can use whatever you like */
#parent {
    width: 100%;
    background: #eeeeee;
}

.column {
    float: left; /* This is forces the page content to adjust to changes in height of the columns */
    overflow: hidden; /* As a precaution, all content is clipped to the column width */
    margin: 0; /* This and padding need to be zero */
    padding: 0;
    z-index: 1;
    background:#E7FECD;
    color: #666666;
    width: 33%; /* Default value, overridden by class */
}

/* Any spacing must be on a child of the column, not the column itself */
.content {
    border: 1px solid black;
    margin: 5px;
    padding: 5px;
}

Finally, you can browse the source in subversion here and read the naturaldocs documentation here.

21 Responses to “Resizeable DIVs (aka SplitPanes)”

  1. yaz Says:

    Hi,
    I am not a programer and it is not very clear for me how to set the things to make an identical effect to the exemple above. However, i desperately need this piece of code to implement it in my company s intra net application.
    I would like to know if it is possible for you to send me an html doc containing the code with all the settings to reproduce what you have done here. That would be very helpful for a me.
    Thanxs
    yaz

  2. yaz Says:

    or at least give me an exemple of what could be filled in the following par ot the code to make it work :
    initialize: function(div1_id, div1_width, div2_id, div2_left, div2_width, options) {
    this.options = {
    onStart: Prototype.emptyFunction,
    onDrag: Prototype.emptyFunction,
    onEnd: Prototype.emptyFunction,
    active: false
    }

    Object.extend(this.options, options || {});

    this.div1 = $(div1_id);
    this.div2 = $(div2_id);
    this.container = this.div1.parentNode; // This had better be the same for both divs
    this.div1_width = div1_width; // as a percentage
    this.div2_left = div2_left; // as a percentage
    this.div2_width = div2_width; // as a percentage
    thanks
    yaz

  3. judge Says:

    You don’t fill in that part of the code. The article gives examples of what to do, plus you can look at the source to this page. I think you probably need some Javascript programming experience to be able to use this.

  4. yaz Says:

    I think that i need to install the script.aculo.us library and other files coming in the zip on my site. I ll try that and if it works i ll leave the solution here

  5. Biggieman Says:

    Hello.

    Big thanks for this script. But i have trouble in IE6 with start position anf height of dividers

    They are not 100% height on load. In FF – ok.

  6. Biggieman Says:

    I’m use your “splitpane.js” with “layout_manager.js” from http://blog.xilinus.com/2007/10/3/simple-layout-manager-with-prototype-1-6

    Look at my example: http://frogtail.ru/splitpanes/test.html

  7. judge Says:

    It looks as though you are using some javascript to set the height of those divs. You probably need to call setDividerHeight() on each splitpane instance after the div heights have been set. You can use some code like the following:

    for(i=0; i<SplitPane.cache.length; i++){
    SplitPane.cache[i].setDividerHeight();
    }
  8. Biggieman Says:

    =) Yeah) It works)

    I removed “onload” methods from “layout_manager” and “splitpane” and merged in one.

    Event.observe(window, “load”, function() {
    layoutManager = new LayoutManager();
    SplitPane.setAll();
    })

    Thanks…

  9. Alex Says:

    I’m using the 3 column layout above with the 2 dividers. Normally it works fine but I’ve added an FCKEditor in the 3rd column and this is causing a problem. When I move a divider (with an FCKEditor on its right side) to the right , the mouse usually ‘beats’ the divider and ends up going into the FCKEditor. The events are then consumed by FCKEditor and the divider stops working. So my question is: Is there a way to dynamically disable the FCKEditor (or any other javascript object) while moving the divider?

  10. Dan Says:

    Alex,

    Did you ever resolve your problem with the FCKEditor? I’ve a similar issue with dragging and dropping into the editor. I half resolved it by modfying dragdrop.js to attach an extra mousemove event to the FCKEditor IFrame, but it’s still not perfect.

  11. Toni Van de Voorde Says:

    Hi,

    First of all I want to thank you for this api. We use it now in our project. But I made some changes because I had some bugs, and needed more features.

    Bugfix: In firefox when scrollbar appears the divider isn’t reset correctly.
    - Added an onmouse over and onmouse out effect.
    - Added new options:
    > div1MinWidth – The minimum width of the first div.
    > div2MinWidth – The minimum width of the second div.
    > dividerBlockedColor – Color of the divider when the div has reach his minimum width
    > dividerHoverColor – Color to display on Mouse Over of the divider.

    Here is an example of it http://www.mg-x.eu/javascript/splitpane/ and the api http://www.mg-x.eu/javascript/splitpane/splitpane-patched.js.

    Let me know what you think about it

    Ciao

  12. judge Says:

    They look like good additions.

  13. Diane Benz Says:

    I just found this javascript while looking for the same functionality for a horizontal divider: i.e. I have a top and bottom div that I want to be able to resize with a divider. I wonder if this script can be easily modified (or a parameter passed in) to switch between vertical and horizontal division.

  14. judge Says:

    Well, it can certainly be modified. But I’m not sure about the ‘easy’ part – that would depend on how familiar you are with JavaScript/HTML/CSS :-)

  15. Jean-Philippe Encausse Says:

    Would it be possible to do something like that:
    http://ajaxian.com/archives/eclipse-like-dockable-frames-using-javaline

    Does anybody have a bootstrap code ?

  16. judge Says:

    The code doesn’t support horizontal dividers, though actually that would help me immensely if it did. So the answer is ‘no’, it can’t be used to implement the Javelin panel layout. BTW Javelin looks like an awesome library. The namespace/declarative approach is pretty stunning and the rendered results are pretty awesome too. If I were developing my web-site now I would certainly give that a go.

  17. Ian Connor Says:

    If you have a PDF in the mix, the makePosition breaks it on OS X (Safari and FF). So, I changed the set function so that it only does this on IE (as that seems to be the only place it matters).

    if (navigator.userAgent.toLowerCase().indexOf(“msie”) != -1) {
    Element.makePositioned(this.container); // Fix IE but don’t break FF
    }

    Also, on Safari, in some cases, setAll does not run on the window load event. So, calling it directly seems to get it working:

    SplitPane.setAll();

  18. Iain Mcleod Says:

    Hi Judge.

    Firstly, thank you for posting this code. It’s a nice clean implementation and exactly what I was looking for.

    Can you please clarify whether it is freely available for reuse? As there is no accompanying license as far as I can see.

    Regards
    Iain

  19. judge Says:

    Yes, you can use it however you want.

  20. Iain Mcleod Says:

    Thanks for that, I’m eternally in your debt!
    IM

  21. miro Says:

    Hi from paris/france

    I have a little bug with your page (IE + FF + Chrome…) :

    When you strech the right column to zero by extending the middle column THEN you strech the middle column by extending the left colum, U have only one column (the left) with the dividers at the right.

    OK but U will be not able to resize the columns……..

    Good job, Thk

Leave a Reply