[javascript] Best way to do a split pane in HTML

Is there a good technique to make a resizable split pane in HTML?

May it be done using CSS / jQuery / JavaScript or is there a good JavaScript library that have been used?

(An example of a split pane is the favorites bar in Internet Explorer which you may have docked to the left of your main browser window.)

This question is related to javascript html css

The answer is


A good library is Shield UI - you can take a look at their flexible Splitter widget and the rest of the powerful components the framework offers.


You can do it with jQuery UI without another JavaScript library. Just add a function to the .resizable resize event to adjust the width of the other div.

$("#left_pane").resizable({
  handles: 'e', // 'East' side of div draggable
  resize: function() {
    $("#right_pane").outerWidth( $("#container").innerWidth() - $("#left_pane").outerWidth() );
  }
});

Here's the complete JSFiddle.


One totally different approach is to put things in a grid, such as ui-grid or Kendo's grid, and have the columns be resizable. A downside is that users would not be able to resize the rows, though the row size could be set programmatically.


Many missed this post from Barguast on Feb 27 '15 where shows a interesting generic flexbox vertical and horizontal resizer.

Take a look: Flexbox Resizing

Barguast note that "... it only handles items sized with flex-grow. If flex-shrink or flex-basis is defined, then the calculations simply don't work.", and he is looking for a better solution, so do I.

Here is his code for reference:

_x000D_
_x000D_
function manageResize(md, sizeProp, posProp)
{
    var r = md.target;

    var prev = r.previousElementSibling;
    var next = r.nextElementSibling;
    if (!prev || !next) {
        return;
    }

    md.preventDefault();

    var prevSize = prev[sizeProp];
    var nextSize = next[sizeProp];
    var sumSize = prevSize + nextSize;
    var prevGrow = Number(prev.style.flexGrow);
    var nextGrow = Number(next.style.flexGrow);
    var sumGrow = prevGrow + nextGrow;
    var lastPos = md[posProp];

    function onMouseMove(mm)
    {
        var pos = mm[posProp];
        var d = pos - lastPos;
        prevSize += d;
        nextSize -= d;
        if (prevSize < 0) {
            nextSize += prevSize;
            pos -= prevSize;
            prevSize = 0;
        }
        if (nextSize < 0) {
            prevSize += nextSize;
            pos += nextSize;
            nextSize = 0;
        }

        var prevGrowNew = sumGrow * (prevSize / sumSize);
        var nextGrowNew = sumGrow * (nextSize / sumSize);

        prev.style.flexGrow = prevGrowNew;
        next.style.flexGrow = nextGrowNew;

        lastPos = pos;
    }

    function onMouseUp(mu)
    {
        window.removeEventListener("mousemove", onMouseMove);
        window.removeEventListener("mouseup", onMouseUp);
    }

    window.addEventListener("mousemove", onMouseMove);
    window.addEventListener("mouseup", onMouseUp);
}

function setupResizerEvents()
{
    document.body.addEventListener("mousedown", function (md) {
        var target = md.target;
        if (target.nodeType !== 1 || target.tagName !== "FLEX-RESIZER") {
            return;
        }
        var parent = target.parentNode;
        var h = parent.classList.contains("h");
        var v = parent.classList.contains("v");
        if (h && v) {
            return;
        } else if (h) {
            manageResize(md, "scrollWidth", "pageX");
        } else if (v) {
            manageResize(md, "scrollHeight", "pageY");
        }
    });
}

setupResizerEvents();
_x000D_
flex {
    display: flex;
}

flex-item > flex {
    position: absolute;
    width: 100%;
    height: 100%;
}

flex.h {
    -ms-flex-direction: row;
    flex-direction: row;
}

flex.v {
    -ms-flex-direction: column;
    flex-direction: column;
}

flex-item {
    display: flex;
    position: relative;
    overflow: hidden;
}

flex > flex-resizer {
    -ms-flex: 0 0 8px;
    flex: 0 0 8px;
    background: white;
}

flex.h > flex-resizer {
    cursor: ew-resize;
}

flex.v > flex-resizer {
    cursor: ns-resize;
}
_x000D_
<body>
    <flex class="v" style="height: 500px">
        <flex-item style="flex: 1; background: red">Flex 1</flex-item>
        <flex-resizer></flex-resizer>
        <flex-item style="flex: 1; background: blue">
            <flex class="h">
                <flex-item style="flex: 1">Flex 2</flex-item>
                <flex-resizer></flex-resizer>
                <flex-item style="flex: 2; background: green">
                    <flex class="v">
                        <flex-item style="flex: 1; background: pink;">Flex 3</flex-item>
                        <flex-resizer></flex-resizer>
                        <flex-item style="flex: 1">
                            <flex class="h">
                                <flex-item style="flex: 1">Flex 4</flex-item>
                                <flex-resizer></flex-resizer>
                                <flex-item style="flex: 2; background: yellow">Flex 5</flex-item>
                                <flex-item style="flex: 2; background: yellow">Flex 6</flex-item>
                            </flex>
                        </flex-item>
                    </flex>
                </flex-item>
            </flex>
        </flex-item>
    </flex>
</body>
_x000D_
_x000D_
_x000D_

And here is my improved version:

_x000D_
_x000D_
function manageResize(md, sizeProp, posProp) {
    var r = md.target;

    var prev = r.previousElementSibling;
    var next = r.nextElementSibling;
    if (!prev || !next) {
        return;
    }

    md.preventDefault();

    var prevSize = prev[sizeProp];
    var nextSize = next[sizeProp];
    var sumSize = prevSize + nextSize;
    var prevGrow = Number(prev.style.flexGrow);
    var nextGrow = Number(next.style.flexGrow);
    var sumGrow = prevGrow + nextGrow;
    var lastPos = md[posProp];

    function onMouseMove(mm) {
        var pos = mm[posProp];
        var d = pos - lastPos;
        prevSize += d;
        nextSize -= d;
        if (prevSize < 0) {
            nextSize += prevSize;
            pos -= prevSize;
            prevSize = 0;
        }
        if (nextSize < 0) {
            prevSize += nextSize;
            pos += nextSize;
            nextSize = 0;
        }

        var prevGrowNew = sumGrow * (prevSize / sumSize);
        var nextGrowNew = sumGrow * (nextSize / sumSize);

        prev.style.flexGrow = prevGrowNew;
        next.style.flexGrow = nextGrowNew;

        lastPos = pos;
    }

    function onMouseUp(mu) {
        // Change cursor to signal a state's change: stop resizing.
        const html = document.querySelector('html');
        html.style.cursor = 'default';

        if (posProp === 'pageX') {
            r.style.cursor = 'ew-resize'; 
        } else {
            r.style.cursor = 'ns-resize';
        }
        
        window.removeEventListener("mousemove", onMouseMove);
        window.removeEventListener("mouseup", onMouseUp);
    }

    window.addEventListener("mousemove", onMouseMove);
    window.addEventListener("mouseup", onMouseUp);
}

function setupResizerEvents() {
    document.body.addEventListener("mousedown", function (md) {

        // Used to avoid cursor's flickering
        const html = document.querySelector('html');
        
        var target = md.target;
        if (target.nodeType !== 1 || target.tagName !== "FLEX-RESIZER") {
            return;
        }
        var parent = target.parentNode;
        var h = parent.classList.contains("h");
        var v = parent.classList.contains("v");
        if (h && v) {
            return;
        } else if (h) {
            // Change cursor to signal a state's change: begin resizing on H.
            target.style.cursor = 'col-resize';
            html.style.cursor = 'col-resize'; // avoid cursor's flickering

            // use offsetWidth versus scrollWidth (and clientWidth) to avoid splitter's jump on resize when a flex-item content overflow (overflow: auto).
            manageResize(md, "offsetWidth", "pageX");
            
        } else if (v) {
            // Change cursor to signal a state's change: begin resizing on V.
            target.style.cursor = 'row-resize';
            html.style.cursor = 'row-resize'; // avoid cursor's flickering

            manageResize(md, "offsetHeight", "pageY");
        }
    });
}

setupResizerEvents();
_x000D_
body {
    /* margin:0; */
    border: 10px solid #aaa;
}

flex {
    display: flex;
    overflow: hidden;
}

/* flex-item > flex {
    position: absolute;
    width: 100%;
    height: 100%;
} */

flex.h {
    flex-direction: row;
}

flex.v {
    flex-direction: column;
}

flex-item {
    /* display: flex; */
    /* position: relative; */
    /* overflow: hidden; */
    overflow: auto;
}

flex > flex-resizer {
    flex: 0 0 10px;
    /* background: white; */
    background-color: #aaa;
    background-repeat: no-repeat;
    background-position: center;
}

flex.h > flex-resizer {
    cursor: ew-resize;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='30'><path d='M2 0 v30 M5 0 v30 M8 0 v30' fill='none' stroke='black'/></svg>");
}

flex.v > flex-resizer {
    cursor: ns-resize;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='10'><path d='M0 2 h30 M0 5 h30 M0 8 h30' fill='none' stroke='black'/></svg>");
}
_x000D_
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>flex-splitter</title>
    <link rel="stylesheet" href="./src/styles.css">
    <script src="./src/index.js" defer></script>
</head>

<body>
    <flex class="v" style="flex: 1; height: 500px;">
        <flex-item style="flex: 1;">Flex 1</flex-item>
        <flex-resizer></flex-resizer>
        <flex class="h" style="flex: 1;">
            <flex-item style="flex: 1; background-color: aqua;">
      
      <!-- 
        The next section is an example to test the splitter when there is content inside a flex-item
      -->
        <section>
                    <div>
                        <label for="CursorCoor" style="display: block;">showCursorCoor: </label>
                        <textarea id="CursorCoor" rows="6" cols="50" wrap="soft" readonly></textarea>
                    </div>
                
                    <br />
                
                    <div>
                        <label for="boxInfo" style="display: block;">showBoxInfo: </label>
                        <textarea id="boxInfo" rows="6" cols="50" wrap="soft" readonly></textarea>
                    </div>
                </section>
        
      </flex-item>
            <flex-resizer></flex-resizer>
            <flex class="v" style="flex: 2; ">
                <flex-item style="flex: 1; background: pink;">Flex 3</flex-item>
                <flex-resizer></flex-resizer>
                <flex class="h" style="flex: 1">
                    <flex-item style="flex: 1; background: green;">Flex 4</flex-item>
                    <flex-resizer></flex-resizer>
                    <flex-item style="flex: 2;">Flex 5</flex-item>
                    <!-- <flex-resizer></flex-resizer> -->
                    <flex-item style="flex: 3; background: darkorange;">Flex 6</flex-item>
                </flex>
            </flex>
        </flex>
    </flex>
    
</body>
</html>
_x000D_
_x000D_
_x000D_

Or see it on Codesandbox:

Edit sad-butterfly-1fwo4


The Angular version with no third-party libraries (based on personal_cloud's answer):

_x000D_
_x000D_
import { Component, Renderer2, ViewChild, ElementRef, AfterViewInit, OnDestroy } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})

export class AppComponent implements AfterViewInit, OnDestroy {

  @ViewChild('leftPanel', {static: true})
  leftPanelElement: ElementRef;

  @ViewChild('rightPanel', {static: true})
  rightPanelElement: ElementRef;

  @ViewChild('separator', {static: true})
  separatorElement: ElementRef;

  private separatorMouseDownFunc: Function;
  private documentMouseMoveFunc: Function;
  private documentMouseUpFunc: Function;
  private documentSelectStartFunc: Function;

  private mouseDownInfo: any;

  constructor(private renderer: Renderer2) {
  }

  ngAfterViewInit() {

    // Init page separator
    this.separatorMouseDownFunc = this.renderer.listen(this.separatorElement.nativeElement, 'mousedown', e => {

      this.mouseDownInfo = {
        e: e,
        offsetLeft: this.separatorElement.nativeElement.offsetLeft,
        leftWidth: this.leftPanelElement.nativeElement.offsetWidth,
        rightWidth: this.rightPanelElement.nativeElement.offsetWidth
      };

      this.documentMouseMoveFunc = this.renderer.listen('document', 'mousemove', e => {
        let deltaX = e.clientX - this.mouseDownInfo.e.x;
        // set min and max width for left panel here
        const minLeftSize = 30;
        const maxLeftSize =  (this.mouseDownInfo.leftWidth + this.mouseDownInfo.rightWidth + 5) - 30;

        deltaX = Math.min(Math.max(deltaX, minLeftSize - this.mouseDownInfo.leftWidth), maxLeftSize - this.mouseDownInfo.leftWidth);

        this.leftPanelElement.nativeElement.style.width = this.mouseDownInfo.leftWidth + deltaX + 'px';
      });

      this.documentSelectStartFunc = this.renderer.listen('document', 'selectstart', e => {
        e.preventDefault();
      });

      this.documentMouseUpFunc = this.renderer.listen('document', 'mouseup', e => {
        this.documentMouseMoveFunc();
        this.documentSelectStartFunc();
        this.documentMouseUpFunc();
      });
    });
  }

  ngOnDestroy() {
    if (this.separatorMouseDownFunc) {
      this.separatorMouseDownFunc();
    }

    if (this.documentMouseMoveFunc) {
      this.documentMouseMoveFunc();
    }

    if (this.documentMouseUpFunc) {
      this.documentMouseUpFunc();
    }

    if (this.documentSelectStartFunc()) {
      this.documentSelectStartFunc();
    }
  }
}
_x000D_
.main {
  display: flex;
  height: 400px;
}

.left {
  width: calc(50% - 5px);
  background-color: rgba(0, 0, 0, 0.1);
}

.right {
  flex: auto;
  background-color: rgba(0, 0, 0, 0.2);
}

.separator {
  width: 5px;
  background-color: red;
  cursor: col-resize;
}
_x000D_
<div class="main">
  <div class="left" #leftPanel></div>
  <div class="separator" #separator></div>
  <div class="right" #rightPanel></div>
</div>
_x000D_
_x000D_
_x000D_

Running example on Stackblitz


I wanted a vanilla, lightweight (jQuery UI Layout weighs in at 185 KB), no dependency option (all existing libraries require jQuery), so I wrote Split.js.

It weights less than 2 KB and does not require any special markup. It supports older browsers back to Internet Explorer 9 (or Internet Explorer 8 with polyfills). For modern browsers, you can use it with Flexbox and grid layouts.


I found a working splitter, http://www.dreamchain.com/split-pane/, which works with jQuery v1.9. Note I had to add the following CSS code to get it working with a fixed bootstrap navigation bar.

fixed-left {
    position: absolute !important; /* to override relative */
    height: auto !important;
    top: 55px; /* Fixed navbar height */
    bottom: 0px;
}

In the old days, you would use frames to achieve this. There are several reasons why this approach is not so good. See Reece's response to Why are HTML frames bad?. See also Jakob Nielson's Why Frames Suck (Most of the Time).

A somewhat newer approach is to use inline frames. This has pluses and minuses as well: Are iframes considered 'bad practice'?

An even better approach is to use fixed positioning. By placing the navigation content (e.g. the favorites links in your example) in a block element (like a div) then applying position:fixed to that element and setting the left, top and bottom properties like this:

#myNav {
    position: fixed;
    left: 0px;
    top: 0px;
    bottom: 0px;
    width: 200px;
}

... you will achieve a vertical column down the left side of the page that will not move when the user scrolls the page.

The rest of the content on the page will not "feel" the presence of this nav element, so it must take into account the 200px of space it occupies. You can do this by placing the rest for the content in another div and setting margin-left:200px;.


Improving on Reza's answer:

  • prevent the browser from interfering with a drag
  • prevent setting an element to a negative size
  • prevent drag getting out of sync with the mouse due to incremental delta interaction with element width saturation

_x000D_
_x000D_
<html><head><style>

.splitter {
    width: 100%;
    height: 100px;
    display: flex;
}

#separator {
    cursor: col-resize;
    background-color: #aaa;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='30'><path d='M2 0 v30 M5 0 v30 M8 0 v30' fill='none' stroke='black'/></svg>");
    background-repeat: no-repeat;
    background-position: center;
    width: 10px;
    height: 100%;

    /* Prevent the browser's built-in drag from interfering */
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

#first {
    background-color: #dde;
    width: 20%;
    height: 100%;
    min-width: 10px;
}

#second {
    background-color: #eee;
    width: 80%;
    height: 100%;
    min-width: 10px;
}

</style></head><body>

<div class="splitter">
    <div id="first"></div>
    <div id="separator" ></div>
    <div id="second" ></div>
</div>

<script>

// A function is used for dragging and moving
function dragElement(element, direction)
{
    var   md; // remember mouse down info
    const first  = document.getElementById("first");
    const second = document.getElementById("second");

    element.onmousedown = onMouseDown;

    function onMouseDown(e)
    {
        //console.log("mouse down: " + e.clientX);
        md = {e,
              offsetLeft:  element.offsetLeft,
              offsetTop:   element.offsetTop,
              firstWidth:  first.offsetWidth,
              secondWidth: second.offsetWidth
             };

        document.onmousemove = onMouseMove;
        document.onmouseup = () => {
            //console.log("mouse up");
            document.onmousemove = document.onmouseup = null;
        }
    }

    function onMouseMove(e)
    {
        //console.log("mouse move: " + e.clientX);
        var delta = {x: e.clientX - md.e.clientX,
                     y: e.clientY - md.e.clientY};

        if (direction === "H" ) // Horizontal
        {
            // Prevent negative-sized elements
            delta.x = Math.min(Math.max(delta.x, -md.firstWidth),
                       md.secondWidth);

            element.style.left = md.offsetLeft + delta.x + "px";
            first.style.width = (md.firstWidth + delta.x) + "px";
            second.style.width = (md.secondWidth - delta.x) + "px";
        }
    }
}


dragElement( document.getElementById("separator"), "H" );

</script></body></html>
_x000D_
_x000D_
_x000D_


Hmm, I came across this property in CSS 3. This might be easier to use.

CSS resize Property


You can use absolute of fixed positioning. This CSS for example will dock a 2em-bar on the left side of your page:

body {
    padding-left: 2.5em;
}
body > #bar {
    position:fixed;
    top:0; left:0;
    width: 2em;
    height: 100%;
    border-right: 2px solid #55F; background: #ddd;
}

(Demo at jsfiddle.net)


Simplest HTML + CSS accordion, with just CSS resize.

_x000D_
_x000D_
div {
  resize: vertical;
  overflow: auto;
  border: 1px solid
}
.menu {
  display: grid
  /* Try height: 100% or height: 100vh */
}
_x000D_
<div class="menu">
  <div>
    Hello, World!
  </div>
  <div>
    Hello, World!
  </div>
  <div>
    Hello, World!
  </div>
</div>
_x000D_
_x000D_
_x000D_


Simplest HTML + CSS vertical resizable panes:

_x000D_
_x000D_
div {
  resize: horizontal;
  overflow: auto;
  border: 1px solid;
  display: inline-flex;
  height: 90vh
}
_x000D_
<div>
  Hello, World!
</div>
<div>
  Hello, World!
</div>
_x000D_
_x000D_
_x000D_


The plain HTML, details element!.

_x000D_
_x000D_
<details>
  <summary>Morning</summary>
  <p>Hello, World!</p>
</details>
<details>
  <summary>Evening</summary>
  <p>How sweat?</p>
</details>
_x000D_
_x000D_
_x000D_


Simplest HTML + CSS topbar foldable menu

_x000D_
_x000D_
div{
 display: flex
}
summary,p{
 margin: 0px 0 -1px 0px;
 padding: 0 0 0 0.5rem;
 border: 1px black solid
}
summary {
  padding: 0 1rem 0 0.5rem
}
_x000D_
<div>
  <details>
    <summary>FILE</summary>
    <p>Save</p>
    <p>Save as</p>
  </details>
  <details>
    <summary>EDIT</summary>
    <p>Pump</p>
    <p>Transfer</p>
    <p>Review</p>
    <p>Compile</p>
  </details>
  <details>
    <summary>PREFERENCES</summary>
    <p>How sweat?</p>
    <p>Powered by HTML</p>
  </details>
</div>
_x000D_
_x000D_
_x000D_


Fixed bottom menu bar, unfolding upward.

_x000D_
_x000D_
div{
 display: flex;
 position: fixed;
 bottom: 0;
 transform: rotate(180deg)
}
summary,p{
 margin: 0px 0 -1px 0px;
 padding: 0 0 0 0.5rem;
 border: 1px black solid;
 transform: rotate(180deg)
}
summary {
  padding: 0 1rem 0 0.5rem;
}
_x000D_
<div>
  <details>
    <summary>FILE</summary>
    <p>Save</p>
    <p>Save as</p>
  </details>
  <details>
    <summary>EDIT</summary>
    <p>Pump</p>
    <p>Transfer</p>
    <p>Review</p>
    <p>Compile</p>
  </details>
  <details>
    <summary>PREF</summary>
    <p>How?</p>
    <p>Power</p>
  </details>
</div>
_x000D_
_x000D_
_x000D_


Simplest resizable pane, using JavaScript.

_x000D_
_x000D_
let ismdwn = 0
rpanrResize.addEventListener('mousedown', mD)

function mD(event) {
  ismdwn = 1
  document.body.addEventListener('mousemove', mV)
  document.body.addEventListener('mouseup', end)
}

function mV(event) {
  if (ismdwn === 1) {
    pan1.style.flexBasis = event.clientX + "px"
  } else {
    end()
  }
}
const end = (e) => {
  ismdwn = 0
  document.body.removeEventListener('mouseup', end)
  rpanrResize.removeEventListener('mousemove', mV)
}
_x000D_
div {
  display: flex;
  border: 1px black solid;
  width: 100%;
  height: 200px;
}

#pan1 {
  flex-grow: 1;
  flex-shrink: 0;
  flex-basis: 50%; // initial status
}

#pan2 {
  flex-grow: 0;
  flex-shrink: 1;
  overflow-x: auto;
}

#rpanrResize {
  flex-grow: 0;
  flex-shrink: 0;
  background: #1b1b51;
  width: 0.2rem;
  cursor: col-resize;
  margin: 0 0 0 auto;
}
_x000D_
<div>
  <div id="pan1">MENU</div>
  <div id="rpanrResize">&nbsp;</div>
  <div id="pan2">BODY</div>
</div>
_x000D_
_x000D_
_x000D_


Here is my lightweight vanilla JavaScript approach, using Flexbox:

http://codepen.io/lingtalfi/pen/zoNeJp

It was tested successfully in Google Chrome 54, Firefox 50, Safari 10, don't know about other browsers.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.rawgit.com/lingtalfi/simpledrag/master/simpledrag.js"></script>

    <style type="text/css">

        html, body {
            height: 100%;
        }

        .panes-container {
            display: flex;
            width: 100%;
            overflow: hidden;
        }

        .left-pane {
            width: 18%;
            background: #ccc;
        }

        .panes-separator {
            width: 2%;
            background: red;
            position: relative;
            cursor: col-resize;
        }

        .right-pane {
            flex: auto;
            background: #eee;
        }

        .panes-container,
        .panes-separator,
        .left-pane,
        .right-pane {
            margin: 0;
            padding: 0;
            height: 100%;
        }

    </style>

</head>

<body>

<div class="panes-container">
    <div class="left-pane" id="left-pane">
        <p>I'm the left pane</p>
        <ul>
            <li><a href="#">Item 1</a></li>
            <li><a href="#">Item 2</a></li>
            <li><a href="#">Item 3</a></li>
        </ul>
    </div>
    <div class="panes-separator" id="panes-separator"></div>
    <div class="right-pane" id="right-pane">
        <p>And I'm the right pane</p>
        <p>
            Lorem ipsum dolor sit amet, consectetur adipisicing elit. A accusantium at cum cupiditate dolorum, eius eum
            eveniet facilis illum maiores molestiae necessitatibus optio possimus sequi sunt, vel voluptate. Asperiores,
            voluptate!
        </p>
    </div>
</div>


<script>


    var leftPane = document.getElementById('left-pane');
    var rightPane = document.getElementById('right-pane');
    var paneSep = document.getElementById('panes-separator');

    // The script below constrains the target to move horizontally between a left and a right virtual boundaries.
    // - the left limit is positioned at 10% of the screen width
    // - the right limit is positioned at 90% of the screen width
    var leftLimit = 10;
    var rightLimit = 90;


    paneSep.sdrag(function (el, pageX, startX, pageY, startY, fix) {

        fix.skipX = true;

        if (pageX < window.innerWidth * leftLimit / 100) {
            pageX = window.innerWidth * leftLimit / 100;
            fix.pageX = pageX;
        }
        if (pageX > window.innerWidth * rightLimit / 100) {
            pageX = window.innerWidth * rightLimit / 100;
            fix.pageX = pageX;
        }

        var cur = pageX / window.innerWidth * 100;
        if (cur < 0) {
            cur = 0;
        }
        if (cur > window.innerWidth) {
            cur = window.innerWidth;
        }


        var right = (100-cur-2);
        leftPane.style.width = cur + '%';
        rightPane.style.width = right + '%';

    }, null, 'horizontal');


</script>

</body>
</html>

This HTML code depends on the simpledrag vanilla JavaScript lightweight library (less than 60 lines of code).


I wrote simple code for it without any third-party library. This code is only for a horizontal splitter (vertical is the same).

_x000D_
_x000D_
function onload()
{
    dragElement( document.getElementById("separator"), "H" );
}

// This function is used for dragging and moving
function dragElement( element, direction, handler )
{
  // Two variables for tracking positions of the cursor
  const drag = { x : 0, y : 0 };
  const delta = { x : 0, y : 0 };
  /* If present, the handler is where you move the DIV from
     otherwise, move the DIV from anywhere inside the DIV */
  handler ? ( handler.onmousedown = dragMouseDown ): ( element.onmousedown = dragMouseDown );

  // A function that will be called whenever the down event of the mouse is raised
  function dragMouseDown( e )
  {
    drag.x = e.clientX;
    drag.y = e.clientY;
    document.onmousemove = onMouseMove;
    document.onmouseup = () => { document.onmousemove = document.onmouseup = null; }
  }

  // A function that will be called whenever the up event of the mouse is raised
  function onMouseMove( e )
  {
    const currentX = e.clientX;
    const currentY = e.clientY;

    delta.x = currentX - drag.x;
    delta.y = currentY - drag.y;

    const offsetLeft = element.offsetLeft;
    const offsetTop = element.offsetTop;


    const first = document.getElementById("first");
    const second = document.getElementById("second");
    let firstWidth = first.offsetWidth;
    let secondWidth = second.offsetWidth;
    if (direction === "H" ) // Horizontal
    {
        element.style.left = offsetLeft + delta.x + "px";
        firstWidth += delta.x;
        secondWidth -= delta.x;
    }
    drag.x = currentX;
    drag.y = currentY;
    first.style.width = firstWidth + "px";
    second.style.width = secondWidth + "px";
  }
}
_x000D_
.splitter {
    width: 500px;
    height: 100px;
    display: flex;
}

#separator {
    cursor: col-resize;
    background: url(https://raw.githubusercontent.com/RickStrahl/jquery-resizable/master/assets/vsizegrip.png) center center no-repeat #535353;
    width: 10px;
    height: 100px;
    min-width: 10px;
}

#first {
    background-color: green;
    width: 100px;
    height: 100px;
    min-width: 10px;
}

#second {
    background-color: red;
    width: 390px;
    height: 100px;
    min-width: 10px;
}
_x000D_
<html>

    <head>
        <link rel="stylesheet" href="T10-Splitter.css">
        <script src="T10-Splitter.js"></script>
    </head>

    <body onload="onload()">
        <div class="splitter">
            <div id="first"></div>
            <div id="separator"></div>
            <div id="second"></div>
        </div>
    </body>

</html>
_x000D_
_x000D_
_x000D_


Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to html

Embed ruby within URL : Middleman Blog Please help me convert this script to a simple image slider Generating a list of pages (not posts) without the index file Why there is this "clear" class before footer? Is it possible to change the content HTML5 alert messages? Getting all files in directory with ajax DevTools failed to load SourceMap: Could not load content for chrome-extension How to set width of mat-table column in angular? How to open a link in new tab using angular? ERROR Error: Uncaught (in promise), Cannot match any routes. URL Segment

Examples related to css

need to add a class to an element Using Lato fonts in my css (@font-face) Please help me convert this script to a simple image slider Why there is this "clear" class before footer? How to set width of mat-table column in angular? Center content vertically on Vuetify bootstrap 4 file input doesn't show the file name Bootstrap 4: responsive sidebar menu to top navbar Stylesheet not loaded because of MIME-type Force flex item to span full row width