4

I created two versions of a pixel art maker, one with vanilla JS and another with jQuery. When you try drawing on the grid, if you move too quickly, a cell is dragged, and after you release the mouse pointer, it shows the no-symbol (circle with a line through it) and keeps coloring (which doesn't happen otherwise). The only way to make it stop coloring at that point is to click a cell. What code can I add, if any, to prevent this default browser behavior for each version?

I'm not sure if these are what need to be changed, but here are my functions for color dragging:

jQuery (view CodePen for full code):

function dragColor() {
  // Filters clicks by those in cells
  $(pixelCanvas).on('mousedown', 'td', function() {
    mousedown = true;
  });

  // 'mouseup': when pointer is over element and mouse button has been clicked then released (unlike click event, doesn't have to be on same element on which mousedown occurred)
  $(document).mouseup(function() {
    mousedown = false;
  });

  // 'mouseover' triggered when mouse pointer enters an element
  $(pixelCanvas).on('mouseover', 'td', function() {
    if (mousedown) {
      $(this).css('background-color', color);
    }
  });
}

Vanilla JS (view CodePen for full code):

let down = false; // Tracks whether or not mouse pointer is pressed

// Listens for mouse pointer press and release on grid. Changes value to true when pressed, but sets it back to false as soon as released
pixelCanvas.addEventListener('mousedown', function(e) {
    down = true;
    pixelCanvas.addEventListener('mouseup', function() {
        down = false;
    });
  // Ensures cells won't be colored if grid is left while pointer is held down
  pixelCanvas.addEventListener('mouseleave', function() {
    down = false;
  });

  pixelCanvas.addEventListener('mouseover', function(e) {
    // 'color' defined here rather than globally so JS checks whether user has changed color with each new mouse press on cell
    const color = document.querySelector('.color-picker').value;
    // While mouse pointer is pressed and within grid boundaries, fills cell with selected color. Inner if statement fixes bug that fills in entire grid
    if (down) {
      // 'TD' capitalized because element.tagName returns upper case for DOM trees that represent HTML elements
      if (e.target.tagName === 'TD') {
        e.target.style.backgroundColor = color;
      }
    }
  });
});
nCardot
  • 5,992
  • 6
  • 47
  • 83
  • 1
    Can you create a codepen or post your code? It is really hard to help you if we have no code – Tan Duong Mar 27 '18 at 04:19
  • I think this unfortunately needs a complete different solution, you may interest on this thread: [javascript-jquery-limit-mouse-movement-speed-inside-an-element ...](https://stackoverflow.com/questions/29548443/javascript-jquery-limit-mouse-movement-speed-inside-an-element) – Carr Mar 27 '18 at 06:57

2 Answers2

1

The cell drag is a default action of mouse-down at the element level. You need to make sure the page doesn't react the way it's "supposed to" and instead reacts the way you want it to, by adding two lines to your event listener.

 $(pixelCanvas).on('mousedown', 'td', function(evt) {
    mousedown = true;
    evt.preventDefault(); // prevents any unwanted behavior in the page
    evt.stopImmediatePropagation(); // makes sure that no other event listeners fire from this one event.
  });
Patrick Gunderson
  • 3,263
  • 17
  • 28
  • I added that but it still happened. Thanks for trying though. – nCardot Mar 28 '18 at 14:34
  • which browser are you seeing this in? works great on chrome – Patrick Gunderson Mar 29 '18 at 09:06
  • I'm also using Chrome. It works the same way as without the code. Try dragging with the mouse pointer down very fast, then let go and do it again -- for some reason after you click the second time it's much more likely for it to show the no-symbol and keep drawing on mouseup/releasing the mouse pointer. – nCardot Mar 30 '18 at 05:20
1

To stop the default dragging operation you need to call event.preventDefault on the mousedown event.

I simplified your code below to show how this works. If you comment out the e.preventDefault() in the code below then dragging outside the table will select the surrounding text.

With e.preventDefault() no selecting happens when you mousedown within the table.

let down = false; // Tracks whether or not mouse pointer is pressed

var pixelCanvas = document.getElementById('pixelCanvas');

// Stop the drag ad remove the event listeners so we don't keep adding new listeners over and over.
function stopDrag() {
  down = false;
  pixelCanvas.removeEventListener('mouseup', stopDrag);
}

function doMouseLeave() {
// Do you really want to turn off drag here?
//  down = false;
  pixelCanvas.removeEventListener('mouseleave', doMouseLeave);
}

function doMouseOver(e) {
    const color = document.querySelector('.color-picker').value;
    if (down && e.target.tagName === 'TD') {
      e.target.style.backgroundColor = color;
    }
  }

// Listens for mouse pointer press and release on grid. Changes value to true when pressed, but sets it back to false as soon as released
pixelCanvas.addEventListener('mousedown', function(e) {
  down = true;
  pixelCanvas.addEventListener('mouseup', stopDrag);
  pixelCanvas.addEventListener('mouseleave', doMouseLeave);
  pixelCanvas.addEventListener('mouseover', doMouseOver);
  e.preventDefault();
});
#pixelCanvas {
  border: 1px solid black;
  border-collapse: collapse;
}

td {
  border: 1px solid black;
  width: 5px;
  height: 5px;
}
<h3>Color Me</h1>
<table id="pixelCanvas" width="300" height="300">
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
</table>
<hr/>
Color: <input class="color-picker" value="#000000"/>

Do this on the mousemove event to prevent dragging of the cell. Also you don't need evt.stopImmediatePropagation(); since that only prevents other event handlers

Intervalia
  • 10,248
  • 2
  • 30
  • 60