Click and drag

This is the 27th project of WesBos's JS30 series. To see the whole 30 part series, click here Today we make a pretty neat click and drag to scroll interface where you will learn a whole lot about JavaScript events!

Video -

Starter code -

We'll be scrolling horizontally the div.items list

<div class="items">
    <div class="item item1">01</div>
    <div class="item item2">02</div>
    ...
</div>

Don't worry about the CSS, it just positions the items, adds perspective and color. We won't be needing that for our code.


Steps to reach our desired effect -

  1. Detect mouse events
  2. Get initial data (is clicked, initial x, initial Y scroll)
  3. Track drag
  4. Update the scroll position

Detect mouse events

We want to detect the drag only on the items div. mousedown we detect the click, mouseup to detect the release of the click. We only want to detect mousemove when the mouse has been clicked inside div.items. So when the user moves out of the items div, we set isDown to false.

const slider = document.querySelector('.items');
// isDown checks if we have an active click
let isDown = false;

slider.addEventListener('mousedown', (e) => {
  console.log('mousedown')
  let isDown = true
  slider.classList.add('active')
});

slider.addEventListener('mouseleave', () => {
  console.log('mouseleave')
  let isDown = false
  slider.classList.remove('active')
});

slider.addEventListener('mouseup', () => {
  console.log('mouseup')
  let isDown = false
  slider.classList.remove('active')
});

slider.addEventListener('mousemove', (e) => {
  if (!isDown) return;
  console.log('mousemove - drag') //logs only if there is a drag
});

We also add the active class to div.items when the drag is enabled (i.e. isDown == true). The class highlights the background a little bit, changes the cursor, and makes the element slightly bigger.

Get initial data

The moment the user clicks anywhere on the slider, we record the current X position (e.pageX) and the current Y scroll (scrollLeft).

let startX;
let scrollLeft;

slider.addEventListener('mousedown', (e) => {
    isDown = true;
    slider.classList.add('active');
    startX = e.pageX;
    scrollLeft = slider.scrollLeft;
  });

These values will be useful to move the scroll when there is a drag.

Track the drag and update scroll

slider.addEventListener('mousemove', (e) => {
    if (!isDown) return;
    e.preventDefault(); 
    slider.scrollLeft = scrollLeft - (e.pageX - startX);
  });

We have the e.preventDefault() call to stop the default behavior, such as text selection. Now if we drag the mouse to the left in the slider, we want the items to move left as well, for this to happen the scroll should move right. Let's keep it simple and say if we drag 25px left, we want the scroll to move 25px right. (You can later change the sensitivity).

e.pageX gives the new x-position, startX gives us the initial x-position before the drag began. Subtracting that would give us the distance moved horizontally. We then subtract the delta (e.pageX - startX) from the scrollLeft. We would have added the delta if we wanted the scroll to move in the direction of the mouse drag.

We can adjust the sensitivity by multiplying the delta with a positive number greater than 1. Eg. for 2x sensitivity : (e.pageX - startX) * 2

Here is the final code -