DEV Community

Cover image for Vue JS: Draggable div
Andrés Baamonde Lozano
Andrés Baamonde Lozano

Posted on • Edited on

Vue JS: Draggable div

Theese days I have been working on my personal website, i have decided to build an tiny terminal for quering my info. My personal info is also avaiable on the website but a developer's personal website needs to be a bit nerd :D. Website is build with vuejs and bootstrap.

I'm a guy that doesn't like design so my website will not be prettiest in the world but at least, i´m trying to make it functional. Site will be an index of my projects (python libraries especially), usefull links that i been storing during this working years. And of course a CV, but it will be last part.

Component implementation

First, you need implement the component, i choose a component that delegates movement of container on header, you can move function 'drag MouseDown' to modify the behaviour.

Template

Really simple, template with 3 slots for easy custom our component

Script

We will need 3 methods:

  • onclick to start dragging. This method will register two functions to handle movement: onmousemove and mouseup. Also, it will register the first position of the container.
  • onmousemove: Will update position of our container.
  • mouseup: Will delete function handlers for functions onmousemove and itself.

CSS

Only need position absolute and a z-index higher than the other page components

Draggable div component

<template>
  <div ref="draggableContainer" id="draggable-container">
    <div id="draggable-header" @mousedown="dragMouseDown">
      <slot name="header"></slot>
    </div>
    <slot name="main"></slot>
    <slot name="footer"></slot>
  </div>
</template>

<script>
export default {
  name: 'DraggableDiv',
  data: function () {
    return {
      positions: {
        clientX: undefined,
        clientY: undefined,
        movementX: 0,
        movementY: 0
      }
    }
  },
  methods: {
    dragMouseDown: function (event) {
      event.preventDefault()
      // get the mouse cursor position at startup:
      this.positions.clientX = event.clientX
      this.positions.clientY = event.clientY
      document.onmousemove = this.elementDrag
      document.onmouseup = this.closeDragElement
    },
    elementDrag: function (event) {
      event.preventDefault()
      this.positions.movementX = this.positions.clientX - event.clientX
      this.positions.movementY = this.positions.clientY - event.clientY
      this.positions.clientX = event.clientX
      this.positions.clientY = event.clientY
      // set the element's new position:
      this.$refs.draggableContainer.style.top = (this.$refs.draggableContainer.offsetTop - this.positions.movementY) + 'px'
      this.$refs.draggableContainer.style.left = (this.$refs.draggableContainer.offsetLeft - this.positions.movementX) + 'px'
    },
    closeDragElement () {
      document.onmouseup = null
      document.onmousemove = null
    }
  }
}
</script>

<style>
#draggable-container {
  position: absolute;
  z-index: 9;
}
#draggable-header {
  z-index: 10;
}
</style>

Enter fullscreen mode Exit fullscreen mode

Component use example

Components with slots are really easy to use on vue you only need to create a tag template with attribute slot, slot value will be target component

Temmplate

<template>
  <DraggableDiv class="col-11">
    <template slot="header">
      [[[ SOME CONTENT HERE]]]
    </template>
    <template slot="main" >
      [[[ SOME CONTENT HERE]]]
    </template>
    <template slot="footer">
      [[[ SOME CONTENT HERE]]]
    </template>
  </DraggableDiv>
</template>
Enter fullscreen mode Exit fullscreen mode

Script

<script>
import DraggableDiv from './DraggableDiv'
export default {
  components: {
    DraggableDiv
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

Thats all

I used this component to implement a draggable terminal for my personal website, the result looks like this.
Demo drag and drop

If any of you wants a post of my terminal implementation on javascript leave a comment below :D

References

Top comments (7)

Collapse
 
rootster profile image
Andrej Soldo

Hi, great tutorial, can you tell me how to create this to work on mobile with touch but also with mouse, like now as is?

Collapse
 
mandrewcito profile image
Andrés Baamonde Lozano • Edited

I think that hammerjs.github.io/ can resolve your problems :). Did you try it? for me its a must in hybrid apps. If you consider it useful i would consider updating the post

Collapse
 
rootster profile image
Andrej Soldo

I didn't know for hammerjs but I think that's it. That would help me a lot. I will see now what I can do with hammerjs.

Collapse
 
fardisaz profile image
fardisaz • Edited

thanks for this useful article. Can you please tell how I can initiallize the position of the element in the page. Imagin, we have couple of draggable elements in the page , I like to know how I can set the initial position , so I make sure all of the elements are separated initially

Collapse
 
mandrewcito profile image
Andrés Baamonde Lozano

You can pass initial positions via props :

vuejs.org/v2/guide/components-prop...

Collapse
 
calvincani profile image
SnowCrest Digital

I used your code to create a draggable popup box. Inside the popup box are forms for register and login. The draggable functionality disables the forms and I cannot enter any info. Any idea why that is?

Collapse
 
keyur15 profile image
Keyur Kansara

Can you give link of this code displayed in GIF?