Emulate Frameset Separator Behavior
Asked Answered
L

4

4

The HTML5 current specification removed the <frameset> tag.

There is a nice feature of <frameset> which is not easy to reproduce without it:

In a frameset, you can change the position of the line separating the frames with the mouse.

How would I provide the same functionality with with using DIVs in JavaScript?

I've come across the following which demonstrates the behavior I'm looking for. However I would like to avoid using JQuery, even though using jQuery should be the preferred way.

Laurent answered 19/5, 2013 at 14:51 Comment(1)
Deleted the comments here. It's not really on-topic, nor generally helpful, to try to convince someone to use a particular library when they have already stated they don't intend to use it - especially when the same thing can be created without too much fuss otherwise.Credent
S
5

After looking at your example fiddle, I can say that this is actually quite easy without jQuery.

All the functions there are just simple innerHTML and style manipulation, and event subscription.

A direct rewrite of that code without jQuery would be:

var i = 0;

document.getElementById("dragbar").onmousedown = function (e) {

    e.preventDefault();
    document.getElementById("mousestatus").innerHTML = "mousedown" + i++;
    window.onmousemove = function (e) {
        document.getElementById("position").innerHTML = e.pageX + ', ' + e.pageY;
        document.getElementById("sidebar").style.width = e.pageX + 2 + "px";
        document.getElementById("main").style.left = e.pageX + 2 + "px";
    };

    console.log("leaving mouseDown");
};

window.onmouseup = function (e) {
    document.getElementById("clickevent").innerHTML = 'in another mouseUp event' + i++;
    window.onmousemove = null;
};

So here is the same fiddle with pure JS.


EDIT: As @BenjaminGruenbaum pointed out, overriding the on* properties on a DOM element is not the same as specifying a new event handler.

Overriding properties like onmouseup, onload, onclick on DOM elements is the "old" way, and therefore it was supported in even the stone age of JS. My code above was written like that.

Nowadays the standard way of adding and removing event handlers are addEventListener and removeEventListener. They are not supported in old IE (but this can be worked around).

It let's you attach unlimited number of listeners to the same event and they will not interfere with each other.

So the same functionality can be achieved by:

var i = 0;

function dragBarMouseDown(e) {
    e.preventDefault();
    document.getElementById("mousestatus").innerHTML = "mousedown" + i++;
    window.addEventListener("mousemove", windowMouseMove, false);
    console.log("leaving mouseDown");
}

function windowMouseMove(e) {
    document.getElementById("position").innerHTML = e.pageX + ', ' + e.pageY;
    document.getElementById("sidebar").style.width = e.pageX + 2 + "px";
    document.getElementById("main").style.left = e.pageX + 2 + "px";
}

function windowMouseUp(e) {
    document.getElementById("clickevent").innerHTML = 'in another mouseUp event' + i++;
    window.removeEventListener("mousemove", windowMouseMove, false);
}

document.getElementById("dragbar").addEventListener("mousedown", dragBarMouseDown, false);

window.addEventListener("mouseup", windowMouseUp, false);

Fiddle.

Note that in this case my functions are not anonymous, so a self executing function for scoping would make sense here, if you are not already in function scope.

Siusiubhan answered 19/5, 2013 at 15:10 Comment(6)
Thanks, I knew there was a 15 line answer.And that it could be translated from JQuery, except that I did not know who to do it.Laurent
This is a good answer, it's not identical though, note that when you attach an event by assigning a handler to an on type of function like window.onmouseup you're attaching an only event, if you add another window.onmouseup you'd override the older one, this is unlike jQuery's .mouseup , you might want to be looking into addEventListener for being exactly the same.Chatter
Thanks. In other words, in Vanilla-JS you override listener and in jQuery you add listener. Good to know.Laurent
@BenjaminGruenbaum yes this is true, assigning a value to window.onmouseup is not subscribing to an event, but overriding a function which is already subscribed to the event. It can interfere with other scripts depending on onmouseup. I was to lazy to point it out but now I have to incorporate it in my answer.Siusiubhan
You don't have to be ashamed. There is a long time I was looking for a link like your "Consider accepting my answer". Your answer works perfectly.Laurent
Even if it is stone age coding, overriding a listener is supposed to erase the previous behavior. So I keep your first answer. It gives me the flexibility to call the previous handler never, before or after the new one, without having to learn one more part of the language spec.Laurent
M
1

Here is a horizontal version of @SoonDead's pure and simple answer with a bottom shelf and a horizontal divider.

The result should look like this:

enter image description here

fiddle here

var i = 0;

function dragBarMouseDown(e) {
    e.preventDefault();
    document.getElementById("mousestatus").innerHTML = "mousedown" + i++;
    window.addEventListener("mousemove", windowMouseMove, false);
    console.log("leaving mouseDown");
}

function windowMouseMove(e) {
        document.getElementById("position").innerHTML = e.pageX + ', ' + e.pageY;
        //document.getElementById("main").style.height = e.pageY + 2 + "px";
        document.getElementById("dragbar").style.top = e.pageY + 2 + "px";
        document.getElementById("bottomshelf").style.top = e.pageY + 17 + "px";
}

function windowMouseUp(e) {
    document.getElementById("clickevent").innerHTML = 'in another mouseUp event' + i++;
    window.removeEventListener("mousemove", windowMouseMove, false);
}

document.getElementById("dragbar").addEventListener("mousedown", dragBarMouseDown, false);

window.addEventListener("mouseup", windowMouseUp, false);
body, html {
    width:100%;
    height:100%;
    padding:0;
    margin:0;
}
#header {
    background-color: wheat;
    width:100%;
    height: 50px;
}
#main {
    background-color: BurlyWood;
    float: top;
    position: absolute;
    top:50px;
    width:100%;
    bottom: 38px;
    overflow-y: hidden;
}
#dragbar {
    background-color:grey;
    width:100%;
    float: top;
    top:120px;
    bottom:0px;
    height: 15px;
    cursor: row-resize;
    position:absolute;
}
#bottomshelf {
    background-color: IndianRed;
    width:100%;
    float: top;
    position: absolute;
    top:135px;
    bottom: 38px;

}
#footer {
    background-color: PaleGoldenRod;
    width:100%;
    height: 38px;
    bottom:0;
    position:absolute;
}
<div id="header">header <span id="mousestatus"></span>
 <span id="clickevent"></span>

</div>
<div id="main">main area:
The bottom shelf will slide over this.
</div>

<div id="dragbar">drag me up or down</div>

<div id="bottomshelf">
    
<span id="position"></span>

bottom shelf</div>

<div id="footer">footer</div>
Mobocracy answered 4/1, 2015 at 8:28 Comment(0)
H
0

I don't have enough reputation to add comment to "SoonDead"s solutions, so I have to do this. :-( The solutions are great and worked out for me, except for IE8

1) The line e.preventDefault(); has two issues for IE8

  • the argument "e"vent is not defined.
  • preventDefault method is not defined for e

So the above line is replaced with: e = e || window.event; e.preventDefault ? e.preventDefault() : e.returnValue=false;

The above stopped the errors (and I stopped here since I don't really care about IE8 but do not want error boxes popping up for the hapless user). So yes, in my app IE8 users cannot resize.

However, I did chase it down a little, and found these issues:

  1. Code flow did not enter the onmousemove function
  2. The e.pageX will have to be replaced by e.clientX or e.screenX (depending on your case) since I could not see pageX as a property in the IE8 debugger.
Hoard answered 16/3, 2014 at 10:19 Comment(7)
Since I was so close, I got it working with IE8. Had to make the following changes: 1. Change the functions to be be called from onmousedown (on the separator), onmousemove(on the body), onmouseup(on the body)Hoard
Since I was so close, I got it working with IE8. Had to make the following changes: 1. Change the functions to be be called from onmousedown (on the separator), onmousemove(on the body), onmouseup(on the body) 2. Add a global var moving, and set it to true in onmousedown 3. Put all the code within onmousemove within an if (moving) 4. Add var pageX = e.pageX || e.clientX; in onmousemove 5. Change all the e.pageX to pageX in onmousemove 6. moving = false; in onmouseupHoard
<body onMouseMove='f_mouseMove(event)' onMouseUp='f_mouseUp()'> <div id="separator" onmousedown="f_mouseDown(event)"></div>Hoard
var moving = false;Hoard
function f_mouseDown(e) { e = e || window.event; e.preventDefault ? e.preventDefault() : e.returnValue = false; moving = true; } Hoard
function f_mouseMove(e) { if (moving === true) { e = e || window.event; e.preventDefault ? e.preventDefault() : e.returnValue = false; var pageX = e.pageX || e.clientX; document.getElementById("sidebar").style.width = pageX - 5 + "px"; document.getElementById("separator").style.left = pageX + "px"; document.getElementById("mainbox").style.left = pageX + 5 + "px"; } }Hoard
function f_mouseUp() { moving = false; }Hoard
D
-1

Here are some options discussed on SO.

My personal recommendation would be jQuery Resizable.

As BenjaminGruenbaum said in a comment, jQuery is JavaScript. If you don't want to load the full jQuery library, you'll need to find which parts of the jQuery library you need and pull out the source JavaScript to use. It's certainly doable.

Dewar answered 19/5, 2013 at 15:6 Comment(2)
Please see my comment to @BenjaminGruenbaum. you are right : the jQuery solution is probably the best one. Except it is not doable because I simply do not know jQuery. And please don't flame on me that jQuery is a must for everybody. You are probably right on that point too, but it is a deficiency I have learned to live with. I even posted my question under a anonymous username to remain unrecognized.Laurent
I'll only flame you on the lack of attempt to learn it. Don't learn to live with a lack of knowledge when you can learn to improve! /endFlameDewar

© 2022 - 2024 — McMap. All rights reserved.