1

I am trying to make iframes resizable the same way that a frameset with frames are. This is what I am trying to reproduce:

<!DOCTYPE html>
<html>
    <frameset rows="80%,20%">
        <frame src="https://www.w3schools.com" />
        <frame src="https://www.tutorialspoint.com/html/top_frame.htm" />
    </frameset>
</html>

See: Online Html Editor

I don't want to use JavaScript because it's widely claimed the iframe has the same functionality as frames and framesets, see: Obsolete Eements - W3. Its simply bad practice to use JavaScript for something that can be achieved perfectly without.

Using the resize property, is no good because it produces an icon in the bottom right hand corner the same as textarea. See: Resizable Columns without JQuery.

Dan Bray
  • 7,242
  • 3
  • 52
  • 70
  • 2
    iframe demonstrably does *not* have the exact same functionality as frame/frameset. The kind of split-pane dragging you want cannot be achieved without javascript. – Ouroborus Dec 08 '22 at 00:26
  • @Ouroborus it certainly seems that way. It just seems there must somehow be a way, considering the claim that the `frameset` is obsolete and even the claim that tables can be used for the same functionality as `frameset`. Certainly not the same functionality if it has to be fixed with JavaScript. – Dan Bray Dec 08 '22 at 00:33
  • Citation needed regarding the two claims, "widely claimed the iframe has the same functionality as frames and framesets" and "claim that tables can be used for the same functionality as frameset". – Ouroborus Dec 08 '22 at 00:35
  • This can help you: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_css_responsive_iframe_169 – Kairav Thakar Dec 08 '22 at 00:43
  • @Ouroborus It's just what I remember reading when researching a solution. The `frameset` is certainly not obsolete if it's functionality is still useful https://www.w3.org/TR/html5-diff/#obsolete-elements – Dan Bray Dec 08 '22 at 00:48
  • @KairavThakar That's not the same as what I am trying to do. I would like to drag the edge of an `iframe` in the same way as a `frame` in a `frameset` with the `rows` attribute. – Dan Bray Dec 08 '22 at 00:49
  • 1
    I remember reading such claims myself, but that was years ago, around the time the HTML5 spec was being created. The doc you link is a spec and uses "obsolete" to mean "not carried over from the prior spec". You can still use frameset but, in doing so, you'd be stuck in HTML4. If you want similar functionality in HTML5, you'll need to resort to javascript. – Ouroborus Dec 08 '22 at 01:11
  • @Ouroborus there's absolutely no way I want to use `HTML4`. It's easy enough for me to do this in JavaScript. I guess I will have to. It just seems bad practice, when it's implied that it can be done without. I wouldn't consider taking a useful feature out of the specs, the same as it being obsolete though. – Dan Bray Dec 08 '22 at 01:19

1 Answers1

0

I decided to give up trying to do it without JavaScript. I managed to get it to work smoothly by making sure it gets the position of the mouse cursor correctly, even when hovering over the iframe. It requires access to the domain in the iframe to use postMessage to get the mouse coordinates but no problem, I am only going to use this to create debug tools on my own website anyway. It can be resized in all directions by dragging an edge or corner.

<!DOCTYPE html>
<style>
iframe {
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    width: 100%;
    height: 100%;
    border: none;
}
.frame_holder {
    display: block;
    top: 300px;
    left: 200px;
    height: 500px;
    width: 350px;
    position: absolute;
}

.resizer {
    position: absolute;
    width: 7px;
    height: 7px;
    z-index: 2;
}

.resizer.n {
    top: -7px;
    left: 0px;
    width: 100%;
    cursor: n-resize;
}

.resizer.s {
    bottom: -7px;
    left: 0px;
    width: 100%;
    cursor: s-resize;
}

.resizer.w {
    top: 0px;
    left: -7px;
    height:100%;
    cursor: w-resize;
}

.resizer.e {
    top: 0px;
    right: -7px;
    height:100%;
    cursor: e-resize;
}

.resizer.nw {
    top: -7px;
    left: -7px;
    cursor: nw-resize;
}

.resizer.ne {
    top: -7px;
    right: -7px;
    cursor: ne-resize;
}

.resizer.sw {
    bottom: -7px;
    left: -7px;
    cursor: sw-resize;
}

.resizer.se {
    bottom: -7px;
    right: -7px;
    cursor: se-resize;
}
</style>
<body>
    <div class="frame_holder">
        <div class="resizer n"></div>
        <div class="resizer s"></div>
        <div class="resizer w"></div>
        <div class="resizer e"></div>
        <div class="resizer ne"></div>
        <div class="resizer nw"></div>
        <div class="resizer sw"></div>
        <div class="resizer se"></div>
        <iframe class="frame" src="https://stackoverflow.com/questions/74724195/how-can-i-make-an-iframe-resizeable-without-javascript"></iframe>
    </div>
</body>
<script>
$ = typeof $ == "undefined" ? [] : $;
$[window.location.pathname]=function()
{
    window.addEventListener('contextmenu', (e) => {e.preventDefault()});
    const el = document.querySelector(".frame_holder");
    const iframe = document.querySelector(".frame");

    const resizers = document.querySelectorAll(".resizer");
    var currentResizer = 0;
    var prevX;
    var prevY;
    function resize(el, size)
    {
        Object.keys(size).forEach((i) =>
        {
            el.style[i] = size[i] + "px";
        });
    }

    window.onmessage = function(e)
    {
        try
        {
            var msg = JSON.parse(e.data);
        }
        catch (e)
        {
            return;
        }
        if (msg.type == "mouseUp")
        {
            el.isResizing=false;
            //window.removeEventListener("mousemove", mousemove);
            //window.removeEventListener("mouseup", mouseup);
            return
        }
        if (!el.isResizing)
        {
            try
            {
                window.removeEventListener("mousemove", mousemove);
                //window.removeEventListener("mouseup", mouseup);
            }
            catch (e)
            { }
            return;
        }
        const rect = el.getBoundingClientRect();
        let mouseX = msg.mouseX + rect.left;
        let mouseY = msg.mouseY + rect.top;
        //let top = Math.min(rect.top - (prevY - mouseY), 0);
        //let left = Math.min(rect.left - (prevX - mouseX), 0);
        let top = rect.top - (prevY - mouseY);
        let left = rect.left - (prevX - mouseX);
        let width = rect.width + (prevX - mouseX);
        let width1 = rect.width - (prevX - mouseX);
        let height= rect.height - (prevY - mouseY);
        let height1 = rect.height + (prevY - mouseY);
        //let vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
        //let vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)
        let currentResizer = this.currentResizer;
        if (currentResizer.classList.contains("n"))
            resize(el, {top: top, height: height1});
        else if (currentResizer.classList.contains("w"))
            resize(el, {left: left, width: width});
        else if (currentResizer.classList.contains("s"))
            resize(el, {height: height});
        else if (currentResizer.classList.contains("e"))
            resize(el, {width: width1});
        else if (currentResizer.classList.contains("sw"))
            resize(el, {left: left, width: width, height: height});
        else if (currentResizer.classList.contains("ne"))
            resize(el, {top: top, width: width1, height: height1});
        else if (currentResizer.classList.contains("nw"))
            resize(el, {top: top, left: left, width: width, height: height1});
        else if (currentResizer.classList.contains("se"))
            resize(el, {width: width1, height: height});
        else
            resize(el, {left: left, top: top});

        prevX = mouseX;
        prevY = mouseY;
    };

    for (let resizer of resizers)
    {
        if (resizer.classList.contains("n"))
            iframe.style.borderTop = "groove 2px";
        else if (resizer.classList.contains("s"))
            iframe.style.borderBottom = "groove 2px";
        else if (resizer.classList.contains("w"))
            iframe.style.borderLeft = "groove 2px";
        else if (resizer.classList.contains("e"))
            iframe.style.borderRight = "groove 2px";
        resizer.addEventListener("mousedown", mousedown);

        function mousedown(e)
        {
            currentResizer = e.target;
            //window.currentResizer = e.target;
            window.currentResizer = currentResizer;
            el.isResizing = true;
            prevX = e.clientX;
            prevY = e.clientY;

        window.addEventListener("mousemove", mousemove);
        window.addEventListener("mouseup", mouseup);
        //msg.mouseX=344, msg.mouseY=830
        //e.clientX=551, e.clientY=800
        function mousemove(e)
        {
            if (!el.isResizing)
            {
                window.removeEventListener("mousemove", mousemove);
                window.removeEventListener("mouseup", mouseup);
                return;
            }
            const rect = el.getBoundingClientRect();
            //let top = Math.min(rect.top - (prevY - e.clientY), 0);
            //let left = Math.min(rect.left - (prevX - e.clientX), 0);
            let top = rect.top - (prevY - e.clientY);
            let left = rect.left - (prevX - e.clientX);
            let width = rect.width + (prevX - e.clientX);
            let width1 = rect.width - (prevX - e.clientX);
            let height= rect.height - (prevY - e.clientY);
            let height1 = rect.height + (prevY - e.clientY);

            if (currentResizer.classList.contains("n"))
                resize(el, {top: top, height: height1});
            else if (currentResizer.classList.contains("w"))
                resize(el, {left: left, width: width});
            else if (currentResizer.classList.contains("s"))
                resize(el, {height: height});
            else if (currentResizer.classList.contains("e"))
                resize(el, {width: width1});
            else if (currentResizer.classList.contains("sw"))
                resize(el, {left: left, width: width, height: height});
            else if (currentResizer.classList.contains("ne"))
                resize(el, {top: top, width: width1, height: height1});
            else if (currentResizer.classList.contains("nw"))
                resize(el, {top: top, left: left, width: width, height: height1});
            else if (currentResizer.classList.contains("se"))
                resize(el, {width: width1, height: height});
            else
                resize(el, {left: left, top: top});

            prevX = e.clientX;
            prevY = e.clientY;
        }

        function mouseup()
        {
            el.isResizing = false;
            console.log("mouseup");
            window.removeEventListener("mousemove", mousemove);
            //window.removeEventListener("mouseup", mouseup);
            }
        }
    }
 }
 if (document.readyState!='loading') $[window.location.pathname](); else document.addEventListener('DOMContentLoaded',  $[window.location.pathname]);
</script>

This code goes inside the iframe:

if (!window.isTop)
{
    function mouseup(e)
    {
        window.top.postMessage(JSON.stringify({ type: "mouseUp" }), '*');
    }

    function mousemove(e)
    {
        window.top.postMessage(JSON.stringify({ type: "mouseMove", mouseX: e.clientX, mouseY: e.clientY }), '*');
    }
    window.addEventListener("mouseup", mouseup);
    window.addEventListener("mousemove", mousemove);
}
Dan Bray
  • 7,242
  • 3
  • 52
  • 70