skip to Main Content

From Cannot use DRAGGABLE, RESIZEABLE & SELECTABLE at the same time it can be seen, that resizeability and draggability can be combined for a DIV in a jQuery environment.

However, I have the question of how to create a resizeable and draggable DIV with Vanilla JS.

It’s easy, if the div has a structure e.g. with a header, then only the header can be user as handle for dragging and the resize handle in the lower right corner is free to user for resizing. For the user it’s easy to understand, that he has to click on the header, if he wants to drag the whole box.

If the div has only one content, the whole box has to be used as handle for dragging with the side effect, that a click on the resize handle is interpreted as drag event and no resizing is possible.

Small test program for these 2 cases:
https://jsfiddle.net/RanneR/skLvtehc/7/

<h2>Test resizeable and moveable</h2>
<h4>Block1 without header</h4>
<div class="area">
    <div class="block freemover">
        <p>Lorem ipsum dolor sit amet ...</p>
    </div>
</div>
<h4>Block2 with header</h4>
<div class="area">
    <div class="block freemover">
        <h4 class="anchor">Move me</h4>
        <p>Duis autem vel eum iriure dolor ...</p>
    </div>
</div>

Any ideas appreciated.

2

Answers


  1. Chosen as BEST ANSWER

    Based on the example provided by @silver I updated and cleaned my test program. It now works fine. Thanks again.

    https://jsfiddle.net/RanneR/skLvtehc/12/

    class freeMover {
    constructor(elmnt, options) {
        console.log('freeMover - elmnt, options', elmnt, options);
        this.elmnt = elmnt;
        this.anchor = elmnt;
        this.pos = {};
        if ( this.elmnt ) {
            this.mouseDown = this.mouseDown.bind(this)
            this.mouseMove = this.mouseMove.bind(this)
            this.mouseUp = this.mouseUp.bind(this)
        }
        if (options.anchor) {
            const anchor = elmnt.querySelector(options.anchor);
            if (anchor) {this.anchor = anchor;}
            console.log('freeMover - anchor', anchor);
        }
        this.anchor.addEventListener('mousedown', this.mouseDown.bind(this));
    }
    mouseDown(e) {
        e = e || window.event;
        console.log('mouseDown - event', e);
        this.pos.baseX = this.elmnt.offsetLeft - e.clientX;
        this.pos.baseY = this.elmnt.offsetTop - e.clientY;
        this.elmnt.classList.add('dragging');
        document.addEventListener('mouseup', this.mouseUp);
        document.addEventListener('mousemove', this.mouseMove);
        document.addEventListener('mouseleave', this.mouseUp);
        this.elmnt.addEventListener('mouseleave', this.mouseUp);
    }
    mouseMove(e) {
        e = e || window.event;
        this.elmnt.style.position = 'absolute';
        this.elmnt.style.left = (this.pos.baseX + e.clientX) + "px";
        this.elmnt.style.top = (this.pos.baseY + e.clientY) + "px";
    }
    mouseUp() {
        this.elmnt.classList.remove('dragging');
        document.removeEventListener('mouseup', this.mouseUp);
        document.removeEventListener('mousemove', this.mouseMove);
        document.removeEventListener('mouseleave', this.mouseUp);
        this.elmnt.removeEventListener('mouseleave', this.mouseUp);
    }
    

    }


  2. I have created a movable class for my own project. although you have to modify it according to your needs, but should help get you started.

    class lexMovable {
    
        constructor(element, node = [ document ] ) {
            this.node = node
            this.el = element
    
            if ( this.el ) {
                this.mouseDown = this.mouseDown.bind(this)
                this.mouseUp = this.mouseUp.bind(this)
                this.mouseMove = this.mouseMove.bind(this)
            }
        }
    
        setHandle( handle ) {
            this.handle = handle
            return this
        }
    
        setCallback( callback ) {
            this.callback = callback
            return this
        }
        
        mouseDown( event ) {
            if ( this.callback?.start ) this.callback.start( event, this.el )
            else this.el.classList.add('lex-moving')        
            for ( const node of this.node ) {
                node.addEventListener('mouseup', this.mouseUp)
                node.addEventListener('mousemove', this.mouseMove)
                node.addEventListener('mouseleave', this.mouseUp)
            }
            this.el.addEventListener('mouseleave', this.mouseUp)
        }
        
        mouseUp( event ) {
            if ( this.callback?.end ) this.callback.end( event, this.el )
            else this.el.classList.remove('lex-moving')
            for ( const node of this.node ) {
                node.removeEventListener('mouseup', this.mouseUp)
                node.removeEventListener('mousemove', this.mouseMove)
                node.removeEventListener('mouseleave', this.mouseUp)
            }
            this.el.removeEventListener('mouseleave', this.mouseUp)
        }
        
        mouseMove(event) {
            if ( this.callback?.move ) {
                this.callback.move( event, this.el )
            } else {
                    const style = window.getComputedStyle(this.el)
                const x = (parseFloat(style.getPropertyValue('left')) || 0) + event.movementX
                const y = (parseFloat(style.getPropertyValue('top')) || 0) + event.movementY
                const rect = this.el.getBoundingClientRect()
                const viewHeight = window.innerHeight || document.documentElement.clientHeight
                const viewWidth = window.innerWidth || document.documentElement.clientWidth
                const maxX = viewWidth - rect.width
                const maxY = viewHeight - rect.height
                const constrainedX = Math.max(0, Math.min(x, maxX))
                const constrainedY = Math.max(0, Math.min(y, maxY))
                this.el.style.position = 'absolute'
                this.el.style.top = constrainedY + 'px'
                this.el.style.left = constrainedX + 'px'
            }
        }
        
        run() {
            const handle = this.handle ?? this.el
            handle.addEventListener('mousedown', this.mouseDown)
        }
    
        stop() {
            const handle = this.handle ?? this.el
            handle.removeEventListener('mousedown', this.mouseDown)
        }
    }
    
    const boxes = document.querySelectorAll('.box')
    
    const movable1 = new lexMovable(boxes[0])
    movable1.run()
    const movable2 = new lexMovable(boxes[1])
    const handle = document.querySelector('.heading')
    movable2.setHandle(handle).run()
    .box {
      position: absolute;
      padding: 10px;
      color: #fff;
      resize: both;
      height: 150px;
      width: 150px;
      margin-bottom: 20px;
      background: rgb(75 85 99);
      border: 2px solid #222;
      overflow: auto;
      background-image: linear-gradient(-45deg, red 10px, transparent 10px);
    }
    .second {
      top: 50px;
      left: 50px
    }
    .heading {
      background: red;
      padding: 5px 10px;
    }
    .lex-moving {
      background: orange;
      user-select: none;
      z-index: 2;
    }
    <div class="box">
      I am box
    </div>
    <div class="box second">
      <div class="heading">Move me</div>
      I am box
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search