# Creating a draggable and resizable box

I recently needed to create a small popup element that could be dragged around the page and remember its new position. This is not the behaviour that the default `dragging=true` attribute provides and so wanted to share my solution for this.

For this demo, I have simplified the contents of the popup and divided it into two sub-parts a `.box-header` and a `.box-body` . The solution uses two custom data-attributes`data-draggable` and `data-resizable` that are added to the parent `.box` div.

I prefer to use data-attributes when the behaviour of an element is modifified using javascript as it is very obvious that the javascript and the html markup are tightly coupled. When classes are used to carry out the same purpose, this coupling between the javascript and the CSS is far less obvious. This means that at some point (especially on larger teams) these data-attributes are less likely to be accidentally modified by someone refactoring CSS classes.

```xml
<div class="box" data-draggable="true" data-resizable="true">
  <div class="box-header drag-handle" data-drag-handle="true">Drag here</div>
  <div class="box-body">Draggable and resizable box</div>
</div>
```

Before we go any further, we should note that the draggable feature that we are implementing here is not the native `draggable="true"` attribute that can be used to drag an element's ghost as shown in the code lab below. Dragging the box will create a ghost of the original element and it is the ghost that is moved around the screen.

<iframe height="500" width="100%" src="https://codepen.io/anjalani/embed/wvZzmpW?default-tab=html%2Cresult&amp;editable=true&amp;theme-id=dark">
  See the Pen <a href="https://codepen.io/anjalani/pen/wvZzmpW">
  Creating a draggable and resizable box: Part 1</a> by Anja Lani (<a href="https://codepen.io/anjalani">@anjalani</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe>

Instead, we intend to move the actual box around the screen and then hold the new position after we have stopped dragging. The lab below demonstrates this.

<iframe height="500" width="100%" src="https://codepen.io/anjalani/embed/OJGRvQM?default-tab=js%2Cresult&amp;editable=true&amp;theme-id=dark">
  See the Pen <a href="https://codepen.io/anjalani/pen/OJGRvQM">
  Creating a draggable and resizable box: Part 2</a> by Anja Lani (<a href="https://codepen.io/anjalani">@anjalani</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe>

The `index.html` and `styles.css` files are very straightforward. Let's look at some of the `app.js` code.

#### Setting up the resizable feature

```js
function setupResizable(){
  const resizeEl = document.querySelector('[data-resizable]');
  resizeEl.style.setProperty('resize', 'both');
  resizeEl.style.setProperty('overflow','hidden');
}
```

As we have decided not to use classes to add this functionality to the `.box` element, we add the two styles `resize: both` and `overflow: hidden`. This `resize` CSS property allows us to control how an element can be resized. The `overflow: hidden` CSS property is added as with the default value of `overflow: visible` the `resize` property will be inactive.

#### Implementing the draggable functionality

```js
function dragStart(event){
  dragEl = getDraggableAncestor(event.target);
  dragEl.style.setProperty('position','absolute');
  lastPosition.left = event.target.clientX;
  lastPosition.top = event.target.clientY;
  dragHandleEl.classList.add('dragging');
  dragHandleEl.addEventListener('mousemove', dragMove);
}
```

When an element drag is started, we define the element as `position: absolute` which allows us to set the position relative to its relative ancestor without affecting the flow of the page. In some cases, it might be more suitable to use `position: fixed`. We record the starting position of the mouse or pointer so that we can track how much it has moved since the last position was set. Finally, we add a `mousemove` event listener to take action when the mouse is further moved.

```js
function dragMove(event){
  const dragElRect = dragEl.getBoundingClientRect();
  const newLeft = dragElRect.left + event.clientX - lastPosition.left;
  const newTop = dragElRect.top + event.clientY - lastPosition.top;
  dragEl.style.setProperty('left', `${newLeft}px`);
  dragEl.style.setProperty('top', `${newTop}px`);
  lastPosition.left = event.clientX;
  lastPosition.top = event.clientY;
  window.getSelection().removeAllRanges();
}
```

With each movement of the mouse, we calculate the `getBoundingClientRect()` of the draggable element. Once we know the current position of the `.box` div (our draggable element), we calculate new the `left` and `top` positions using the distance the mouse/pointer has moved since its last position. We can then update the `left` and `top` positions of the `.box` div and the `lastPosition` object values.

In this example, we are also calling `removeAllRanges()` which prevents any text from being selected/highlighted.
