drag and drop, как 'отпустить' блок

Я разбираюсь как сделать drag and drop.
Я хочу при клике мышкой на блок, чтобы блок переносился туда, где мышка, когда мышь отпущена, чтобы блок переставал двигаться к мышке.
Я столкнулся с тем, что не понимаю, почему я не могу отпустить блок.

class BlockInGame {
  constructor(src = 'move__block', styles) {
    this.globalSelectors();
    this.node = document.createElement('div');
    this.node.classList.add(src);
    this.addStyles(styles);
    this.addInBody();
    this.listeners(); // добавление слушателей
  }

  listeners() {
    this.node.ondragstart = () => {
      return false;
    }
    this.node.addEventListener('mousedown', (e) => {
      const localMouseupFunc = (e) => {
        document.removeEventListener('mousemove', this.onMouseMove.bind(this));
        this.node.removeEventListener('mouseup', localMouseupFunc);
      }
      document.addEventListener('mousemove', this.onMouseMove.bind(this));
      this.node.addEventListener('mouseup', localMouseupFunc);
    });
  }

  globalSelectors() {
    if (!this.globalSelectors.body) {
      this.globalSelectors.body = document.querySelector('body');
    }
    if (!this.globalSelectors.game) {
      this.globalSelectors.game = this.globalSelectors.body.querySelector('.game');
    }
  }

  addStyles(styles) {
    for (let key in styles) {
      this.node.style[key] = styles[key];
    }
  }

  addInBody() {
    this.globalSelectors.game.append(this.node);
  }

  move(pageX, pageY) {
    this.node.style.left = pageX - this.node.offsetWidth / 2 + 'px';
    this.node.style.top = pageY - this.node.offsetHeight / 2 + 'px';
  }

  onMouseMove(event) {
    this.move(event.pageX, event.pageY);
  }
}
const block = new BlockInGame(undefined, {
  backgroundColor: 'red',
  width: '80px',
  height: '80px'
});
*,
*::before,
*::after {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-family: 'Poppins', sans-serif;
}

body {
  overflow: hidden;
}

img {
  max-width: 100%;
  display: block;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  -o-user-select: none;
  user-select: none;
}

.container {
  max-width: 900px;
  margin: 0 auto;
}

section {
  background-color: #00cbff;
  min-height: 100vh;
}

.game {
  padding: 20px;
  position: relative;
}

.move__block {
  -webkit-box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.75);
  -moz-box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.75);
  box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.75);
  cursor: pointer;
  position: absolute;
  left: 0;
  top: 0;
}
<section class="game">

</section>


Ответы (1 шт):

Автор решения: Михаил Камахин

Надо было в слушатель передавать просто функцию, а не новую функцию, так как метод .bind возвращает новую функцию

Было:

const localMouseupFunc = (e) => {
    document.removeEventListener('mousemove', this.onMouseMove.bind(this));
    this.node.removeEventListener('mouseup', localMouseupFunc);
}
document.addEventListener('mousemove', this.onMouseMove.bind(this));
this.node.addEventListener('mouseup', localMouseupFunc);

Стало:

const localMouseupFunc = (e) => {
    document.removeEventListener('mousemove', onMouseMoveBind);
    this.node.removeEventListener('mouseup', localMouseupFunc);
}
const onMouseMoveBind = this.onMouseMove.bind(this);
document.addEventListener('mousemove', onMouseMoveBind);
this.node.addEventListener('mouseup', localMouseupFunc);

class BlockInGame {
  constructor(src = 'move__block', styles) {
    this.globalSelectors();
    this.node = document.createElement('div');
    this.node.classList.add(src);
    this.addStyles(styles);
    this.addInBody();
    this.listeners();
  }

  listeners() {
    this.node.ondragstart = function() {
      return false;
    }
    this.node.addEventListener('mousedown', (e) => {
      const localMouseupFunc = (e) => {
        document.removeEventListener('mousemove', onMouseMoveBind);
        this.node.removeEventListener('mouseup', localMouseupFunc);
      }
      const onMouseMoveBind = this.onMouseMove.bind(this);
      document.addEventListener('mousemove', onMouseMoveBind);
      this.node.addEventListener('mouseup', localMouseupFunc);
    });
  }

  globalSelectors() {
    if (!this.globalSelectors.body) {
      this.globalSelectors.body = document.querySelector('body');
    }
    if (!this.globalSelectors.game) {
      this.globalSelectors.game = this.globalSelectors.body.querySelector('.game');
    }
  }

  addStyles(styles) {
    for (let key in styles) {
      this.node.style[key] = styles[key];
    }
  }

  addInBody() {
    this.globalSelectors.game.append(this.node);
  }

  move(pageX, pageY) {
    this.node.style.left = pageX - this.node.offsetWidth / 2 + 'px';
    this.node.style.top = pageY - this.node.offsetHeight / 2 + 'px';
  }

  onMouseMove(event) {
    this.move(event.pageX, event.pageY);
  }
}
const block = new BlockInGame(undefined, {
  backgroundColor: 'red',
  width: '80px',
  height: '80px'
});
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800&display=swap');
*,
*::before,
*::after {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-family: 'Poppins', sans-serif;
}

body {
  overflow: hidden;
}

img {
  max-width: 100%;
  display: block;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  -o-user-select: none;
  user-select: none;
}

.container {
  max-width: 900px;
  margin: 0 auto;
}

section {
  background-color: #00cbff;
  min-height: 100vh;
}

.game {
  padding: 20px;
  position: relative;
}

.move__block {
  -webkit-box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.75);
  -moz-box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.75);
  box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.75);
  cursor: pointer;
  position: absolute;
  left: 0;
  top: 0;
}
<section class="game">

</section>

→ Ссылка