0

I'm implementing a simple horizontal split pane using html, css, and javascript, adapted from this post.

While everything works smoothly for the vertical split pane, the horizontal implementation is jerky. The div sizes and positions jump to an unexpected value, but I am unable to spot when.

The issue is very subtle on below snippet, but some jumpiness and lagging can be observed.

Snippet below:

function dragElement(element, direction) {

    var md;
    const first = document.getElementById("map");
    const second = document.getElementById("table");

    element.onmousedown = onMouseDown;

    function onMouseDown(e) {
        md = {
            e,
            offsetLeft: element.offsetLeft,
            offsetTop: element.offsetTop,
            offsetBottom: element.offsetBottom,
            firstWidth: first.offsetWidth,
            secondWidth: second.offsetWidth,
            firstHeight: first.offsetHeight,
            secondHeight: first.offsetHeight
        };

        document.onmousemove = onMouseMove;

        document.onmouseup = () => {
            document.onmousemove = document.onmouseup = null;
        }
    }

    function onMouseMove(e) {

        var delta = {
            x: e.clientX - md.e.x,
            y: e.clientY - md.e.y
        };

        if (direction === "H") {
            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";
        }

        if (direction === "V") {
            delta.y = Math.min(Math.max(delta.y, - md.firstHeight), md.secondHeight);
            element.style.top = md.offsetTop + delta.y + "px";
            first.style.height = (md.firstHeight + delta.y) + "px";
            second.style.height = (md.secondHeight - delta.y) + "px";
        }
    }
}
dragElement(document.getElementById("separator"), "V");
html,
body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
}

.splitter {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
}


/*for horizontal*/

#separator {
  cursor: row-resize;
  background-color: #aaa;
  background-repeat: no-repeat;
  background-position: center;
  width: 100%;
  height: 10px;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

#map {
  width: 100%;
  height: 20%;
  min-height: 10px;
  padding: 0;
  margin: 0;
}

#table {
  width: 100%;
  height: 20%;
  min-height: 10px;
}
<div class="splitter">
  <div id="map"></div>
  <div id="separator"></div>
  <div id="table"></div>
</div>
divy3993
  • 5,732
  • 2
  • 28
  • 41
user25976
  • 1,005
  • 4
  • 18
  • 39
  • The solution you chose is flawed. It won't go past the double of the first element's height. To test it easily, move the separator as close as you can to top. Then try to move it as close as you can to bottom. – tao May 13 '20 at 01:51
  • Instead of going with the most popular solution, which started out as a SO answer, ended up being improved over 5 years by more than 20 seasoned developers, it's tested in every possible browser, has 0 dependencies, has continuous integration and automated testing, you chose the one which is less popular, hence far less used, hence far less tested, without CI or AT. In the end, the difference between them is that the first one has a lot more development hours invested in it than the second. Both free. And now you're asking others to fix its *"subtle"* flaws. What have ***you*** tried? – tao May 13 '20 at 02:08
  • @tao I see your point. I based my choice mostly on some initial problems I had with the most popular "answer" and the dates of original answers. I can see how the more recent comments on the older answer should have held more weight in my choice. – user25976 May 13 '20 at 02:23
  • Then let's solve problem [X](https://en.wikipedia.org/wiki/XY_problem). :) What is the problem you're trying to solve? Please provide a use-case and all constraints. – tao May 13 '20 at 02:27

1 Answers1

0

The reason is the secondHeight is getting 0 when we move up and down. So I get splitter class height and subtract the firstHeight form that and value equals to secondHeight.

secondHeight: (splitter.offsetHeight - first.offsetHeight)

function dragElement(element, direction) {

    var md;
    const first = document.getElementById("map");
    const second = document.getElementById("table");
    const splitter = document.getElementById("container");

    element.onmousedown = onMouseDown;

    function onMouseDown(e) {
        md = {
            e,
            offsetLeft: element.offsetLeft,
            offsetTop: element.offsetTop,
            offsetBottom: element.offsetBottom,
            firstWidth: first.offsetWidth,
            secondWidth: second.offsetWidth,
            firstHeight: first.offsetHeight,
            secondHeight: (splitter.offsetHeight - first.offsetHeight)
        };
        document.onmousemove = onMouseMove;

        document.onmouseup = () => {
            document.onmousemove = document.onmouseup = null;
        }
    }

    function onMouseMove(e) {

        var delta = {
            x: e.clientX - md.e.x,
            y: e.clientY - md.e.y
        };

        if (direction === "H") {
            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";
        }

        if (direction === "V") {
            delta.y = Math.min(Math.max(delta.y, -md.firstHeight), md.secondHeight);
            element.style.top = md.offsetTop + delta.y + "px";
            first.style.height = (md.firstHeight + delta.y) + "px";
            second.style.height = (md.secondHeight - delta.y) + "px";
        }
    }
}

dragElement(document.getElementById("separator"), "V");
html,
body {
    width: 100%;
    height: 100%;
    padding: 0;
    margin: 0;
}

.splitter {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
}


/*for horizontal*/

#separator {
    cursor: row-resize;
    background-color: #aaa;
    background-repeat: no-repeat;
    background-position: center;
    width: 100%;
    height: 10px;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

#map {
    width: 100%;
    height: 20%;
    min-height: 10px;
    padding: 0;
    margin: 0;
}

#table {
    width: 100%;
    height: 20%;
    min-height: 10px;
}
<div id="container" class="splitter">
    <div id="map"></div>
    <div id="separator"></div>
    <div id="table"></div>
</div>