Full-Screen Pop-Up Window Drag and Drop by Vanilla JavaScript

Question

When I was doing business development recently, I encountered a requirement. When the user opened the pop-up window to fill in the content, he wanted to refer to the content in the blank background of the pop-up window, but the pop-up window blocked his sight, so the user wondered whether he could drag the pop-up window arbitrarily.

In fact, there is more than this scenario. In many form editors, report designers, online ps, and H5 editors, drag is one of the most basic functions.

Next, we will bring a simple experiment and make a simple version of the pop-up window drag and drop function.

Analysis

As you may have thought, there are only two core methods:

  1. Use mousedown, mousemove, mouseup to monitor drag events
  2. Dynamically change the left and top positions of the pop-up window according to the current mouse position when dragging

Just add some detailed experience processing.

Code

1. Constructing the DOM

  • Create a container that occupies a manipulable area
  • Create a pop-up window inside the container to customize the content of the pop-up window
  • At the last position in the pop-up window, place four borders for mounting drag events and monitoring mouse movement
  • Finally, place a pop-up window mask layer to cover the content of the pop-up window when dragging, to prevent the mouse from moving too fast during dragging and triggering events in the pop-up window, such as input and selection events.
<!--Outer container-->
<div class="container">
  <!--Internal pop-up window-->
  <div class="dialog">
    <!--The content of the pop-up window-->
    Pop-up content
    
    <!--Border-->
    <div class="drag-bar-left"></div>
    <div class="drag-bar-right"></div>
    <div class="drag-bar-top"></div>
    <div class="drag-bar-bottom"></div>
    
    <!--Pop-up window mask-->
    <div class="mask-drag"></div>
  </div>
</div>

2. Adjust the style

  • The width and height of the outer container are set to 100%, which can fill its outer parent element. Pay special attention to the use of relative positioning, so that the internal pop-up positioning can take effect
  • The pop-up window needs to use absolute positioning to specify the position. You cannot use a percentage, because you need to change the position of the pop-up window in real time when dragging. At that time, the designated position is used
  • The width of the four inner borders is set to 10px, and they are also fixed to the periphery of the pop-up window with absolute positioning
  • The mask layer is hidden first, and then the pop-up window will be blocked when dragging
.container{
    width:100%;
    height:100%;
    position:relative;
}
.dialog{
    width:150px;
    height:150px;
    position:absolute;
    left:50px;
    top:50px;
    background:gray;
}

.drag-bar-left{
    background:transparent;
    position: absolute;
    left:0;
    top:0;
    bottom:0;
    width:10px;
    cursor: move;
    display: block;
}
.drag-bar-right{
    background:transparent;
    position: absolute;
    right:0;
    top:0;
    bottom:0;
    width:10px;
    cursor: move;
    display: block;
}
.drag-bar-top{
    background:transparent;
    position: absolute;
    left:0;
    top:0;
    right:0;
    height:10px;
    cursor: move;
    display: block;
}
.drag-bar-bottom{
    background:transparent;
    position: absolute;
    left:0;
    right:0;
    bottom:0;
    height:10px;
    cursor: move;
    display: block;
}

/* When dragging, covering the entire bullet box and placing the mouse to trigger the buttons inside */
.mask-drag{
    position: absolute;
    cursor: move;
    user-select: none;
    left:0;
    top:0;
}
      

3. Monitoring events

  • Monitor the mousedown/mousemove/mouseup events of the four borders
  • Store the distance between the mouse position and the left frame of the pop-up window during mousedown diffX = e.clientX-drag.offsetLeft (the same for the upper frame)
  • In the case of mousemove, subtract the distance from the mouse to the left frame according to the mouse position, and the position of the left frame of the pop-up window can be calculated in real time. left = e.clientX-diffX
  • Use a mask layer to enhance the experience
  • Handling browser compatibility
// carried out
initDragDialog();

/*
    * DOM selector
    * @param selector {String} css selector for element
    * @param context {Element} root element
    * usage: $$('head')
    * result: You will get <head> element
    */
function $$(selector, context) {
    context = context || document
    const elements = context.querySelectorAll(selector)
    return elements.length == 1
    ? Array.prototype.slice.call(elements)[0]
    : Array.prototype.slice.call(elements)
}
/**
 * The pop-up window supports dragging
 */
function initDragDialog(){

    // Drag and drop function (mainly to trigger three events: onmousedown\onmousemove\onmouseup)
    const drag = $$('.dialog');
    const divMask = $$('.mask-drag');

    const dragBarTop = $$('.drag-bar-top')
    const dragBarBottom = $$('.drag-bar-bottom')
    const dragBarLeft = $$('.drag-bar-left')
    const dragBarRight = $$('.drag-bar-right')

    dragBarTop.addEventListener('mousedown',mouseDownLisenter)
    dragBarBottom.addEventListener('mousedown',mouseDownLisenter)
    dragBarLeft.addEventListener('mousedown',mouseDownLisenter)
    dragBarRight.addEventListener('mousedown',mouseDownLisenter)

    // When you click on an object, you can use the drag object. Move and up are the global area, that is, the entire document is common. The document object should be used instead of the drag object (otherwise, when the drag object is used, the object can only move to the right or down )

    function mouseDownLisenter (e) {
        e = e || window.event // Compatible with IE browser
        const diffX = e.clientX-drag.offsetLeft // The distance from the moment the mouse clicks on the object relative to the left border of the object = the distance of the position when clicked relative to the leftmost left of the browser-the distance of the left border of the object relative to the leftmost left of the browser
        const diffY = e.clientY-drag.offsetTop

        /* Low version ie bug: When an object is dragged out of the browser’s visual window, a scroll bar will appear,
        The solution is to use two unique methods setCapture()\releaseCapture() in IE browser, these two methods,
        You can let the mouse slide outside the browser to capture the event, and our bug is when the mouse moves out of the browser,
        The function that the limit is exceeded becomes invalid. In this way, this problem can be solved. Note: These two methods are used in onmousedown and onmouseup */
        if (typeof drag.setCapture !=='undefined') {
            drag.setCapture()
        }
        
        // Cover the pop-up window
        divMask.style.width = '100%';
        divMask.style.height = '100%';
        
        // Prevent the selected event from being triggered when the dragging speed is too fast
        document.body.style.userSelect ='none'
        
        document.onmousemove = function (e) {
            e = e || window.event // Compatible with IE browser
            let left = e.clientX-diffX
            let top = e.clientY-diffY

            // 1. Control the range of dragged objects can only be in the browser window, and scroll bars are not allowed
            // if (left <0) {
            //     left = 0
            // } else if (left> window.innerWidth-drag.offsetWidth) {
            //     left = window.innerWidth-drag.offsetWidth
            // }
            // if (top <0) {
            //     top = 0
            // } else if (top> window.innerHeight-drag.offsetHeight) {
            //     top = window.innerHeight-drag.offsetHeight
            // }

            // 2. Allow to drag out of the interface, but keep a part to prevent it from being pulled back
            if (left < -drag.offsetWidth + 100) {
                left = -drag.offsetWidth + 100
            } else if (left > window.innerWidth  - 100) {
                left = window.innerWidth - 100
            }
            if (top < -drag.offsetHeight + 100) {
                top = -drag.offsetHeight + 100
            } else if (top > window.innerHeight - 100) {
                top = window.innerHeight - 100
            }

            // Regain the distance of the object when moving, and solve the phenomenon of shaking when dragging
            drag.style.left = left +'px'
            drag.style.top = top +'px'
            drag.style.transform =''
        }
        document.onmouseup = function (e) {// no longer move when the mouse pops up
            this.onmousemove = null
            this.onmouseup = null // Prevent the mouse from looping after it pops up (that is, prevent the mouse from moving when it is put on)

            // Fix low version ie bug
            if (typeof drag.releaseCapture !=='undefined') {
                drag.releaseCapture()
            }
            
            // Restore the hidden mask layer
            divMask.style.width = '0';
            divMask.style.height = '0';
            document.body.style.userSelect =''
        }
    }
}

Demo

Online Drag and Drop

Conclusion

Basically, the demand for pop-up dragging is realized, and the native js writing method can be quickly used in any front-end framework such as vue/react/angular. This case is relatively concise and can be expanded to suit business functions according to one's own needs.

And when we write a function, we have to learn advanced thinking, such as

  • How to change the size of the pop-up window?
  • How to support drag and drop on the mobile terminal?
  • Can it be extracted as a general tool function?

Time is limited, so when I have time ,I will update my learning experience later.

Comments