Как изменить объект события перед выполнением функции обратного вызова события DOM

<body>
  <button onclick = "myFunction()">CLICK</button>
  <script>
    function myFunction(event) {
      // do something based on the value of the event's property
    }
  </script>
</body>
<body>
  <button id = "myButton">CLICK</button>
  <script>
    var button = document.getElementById("myButton");
    button.addEventListener("click", myFunction);
    function myFunction(event) {
      // do something based on the value of the event's property
    }
  </script>
</body>

Я мог бы добавить события нажатия на кнопку двумя описанными выше способами.

Я хочу изменить некоторые свойства объекта события, который myFunction получит перед выполнением.

Например, в событии клика событие имеет свойства clientX и clientY, и, предполагая, что исходные значения равны 100 и 100, я хочу, чтобы они были 150 и 150 (как они изменяются, определяется определенными условиями), а другие значения свойств быть такими же, как исходные значения.

Есть ли способ добиться этого?


Дополнительная информация

Мне понадобилось измененное событие, потому что по какой-то причине я добавил атрибуты стиля scale, width к элементу body, из-за чего координатные точки, связанные с положением, рассчитывались неправильно.

Некоторые события, связанные с координатными точками, такие как click, drag, ведут себя некорректно.

По сути, каждый объект события, связанный с точкой, должен быть исправлен, особенно MouseEvent, PointerEvent.

Согласно @Mr. Polywhirl, мне нужно сначала написать функцию для обработки объекта события, а затем вызвать фактический обратный вызов в функции обработки, которая более реплицирована.

Можно ли добиться бессмысленной модификации, изменив addEventListener на прототипе?

Если это работает, как привязка, такая как <button onclick = "myFunction(event)">CLICK</button>, может добиться такой же модификации события?

попробуй это

Нажмите F12 на этой странице, чтобы открыть консоль, и введите document.body.style = "scale: 0.8;width: 125%;transform-origin: 0 0;" в консоли. Затем нажмите на recent inbox messages в верхней части страницы, что, вероятно, вы и видите.

Свойства объекта Event доступны только для чтения.

Pointy 07.07.2023 15:11

@ lai9fox ... ОП может проверить поздно предоставленное, но очень удобное решение проблемы ОП.

Peter Seliger 07.07.2023 19:00
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
2
56
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вам нужно будет сделать копию события, так как свойства clientX и clientY доступны только для чтения.

Перехватите событие в другой функции, измените событие и отправьте его исходному обработчику.

var button = document.getElementById('myButton');
button.addEventListener("click", myModifiedFunction);

function myModifiedFunction(event) {
  // The clientX and clientY properties are read-only
  const { clientX, clientY, ...rest } = event;
  
  // We need to make a copy of the event object information
  const eventCopy = { clientX, clientY, ...rest };
  
  // Mutate the (x, y) coordinates with an offset of (1,000, 1,000)
  eventCopy.clientX += 1000;
  eventCopy.clientY += 1000;
  
  // Call the original handler
  myFunction(eventCopy);
}

// Original click handler callback
function myFunction(event) {
  const point = {
    x: event.clientX,
    y: event.clientY
  };
  console.info(`Point: ${JSON.stringify(point)}`);
}
<button onclick = "myFunction(event)">CLICK</button>
<button id = "myButton">CLICK (MODIFIED)</button>

Если вы хотите правильно клонировать событие, вы можете попробовать:

var button = document.getElementById('myButton');
button.addEventListener("click", myModifiedFunction);

function myModifiedFunction(event) {
  // We need to make a copy of the event object information
  const eventCopy = cloneEvent(event);
  
  // Mutate the (x, y) coordinates with an offset of (1,000, 1,000)
  Object.assign(eventCopy, {
    clientX: eventCopy.clientX + 1000,
    clientY: eventCopy.clientY + 1000
  });

  // Call the original handler
  myFunction(eventCopy);
}

// Original click handler callback
function myFunction(event) {
  const point = {
    x: event.clientX,
    y: event.clientY
  };
  console.info(`Point: ${JSON.stringify(point)}`);
}

// Adapted from: https://stackoverflow.com/a/39669794/1762224
function cloneEvent(e) {
  if (!e) return;
  const clone = new Function();
  for (let p in e) {
    let d = Object.getOwnPropertyDescriptor(e, p);
    if (d && (d.get || d.set)) Object.defineProperty(clone, p, d);
    else clone[p] = e[p];
  }
  Object.setPrototypeOf(clone, e);
  return clone;
}
<button onclick = "myFunction(event)">CLICK</button>
<button id = "myButton">CLICK (MODIFIED)</button>

Привет! Я только что добавил некоторую информацию к этому вопросу. Любые другие предложения? @Мистер. Поливихрь

lai9fox 07.07.2023 16:01

@lai9fox нравятся относительные/абсолютные координаты?

Mr. Polywhirl 07.07.2023 16:33

Нажмите F12 на этой странице, чтобы открыть консоль, и введите document.body.style = "scale: 0.8;width: 125%;transform-origin: 0 0;" в консоли. Затем нажмите на последние входящие сообщения в верхней части страницы, что, вероятно, вы и видите.

lai9fox 07.07.2023 16:56

@Mr.Polywhirl ... Объект event не нужно копировать. Нужно просто переопределить clientX и clientY непосредственно в исходной ссылке на событие, например. Object.defineProperties.

Peter Seliger 07.07.2023 18:54
Ответ принят как подходящий

Общее решение с Proxy для изменения события. Вы можете создать глобальный объект-получатель, чтобы исправить значения ваших событий, во фрагменте есть исправление, чтобы сделать event.clientX/Y координаты указателя относительно целевого элемента для всех элементов внутри выбранного элемента контейнера:

var button = document.getElementById('myButton');

const _add = HTMLElement.prototype.addEventListener;

// monkey patch our event handlers for our canvas element only

HTMLElement.prototype.addEventListener = function(name, fn, ...args){
    return _add.call(this, name, event => {
       if (event instanceof MouseEvent && event.target.closest('#\\$canvas')){
        return proxify(clientEvent, fn)(event);
       }
       return fn.call(this, event);
    });
};


// corrects clientX and clientY to be relative to the target element
const clientEvent = {
  clientX(val){
    const rect = this.target.getBoundingClientRect();
    return Math.round(val - rect.left);
  },
  clientY(val){
    const rect = this.target.getBoundingClientRect();
    return Math.round(val - rect.top);
  }
};

// add mouse handlers for the both canvases
'mousemove mousedown mouseup'.split(' ').forEach(name => $canvas.addEventListener(name, myFunction));
'mousemove mousedown mouseup'.split(' ').forEach(name => $canvas2.addEventListener(name, myFunction));

// Original cliekc handler callback
function myFunction(event) {
  const point = {
    x: event.clientX,
    y: event.clientY
  };
  $log.insertBefore(document.createElement('div'), $log.children[0]).textContent = `Point: ${JSON.stringify(point)}`;
}

function proxify(getters, fn) {
  return obj => {
    const proxy = new Proxy(obj, {
      get(target, prop, receiver){
        if (prop in getters){
          return getters[prop].bind(obj)(obj[prop]);
        }
        return obj[prop];
      }
    });
    return fn(proxy);
  }
}
[id=\$canvas]{
  margin:10px;
  background:lightgray;
  border-radius:4px;
  width:200px;
  height:100px;
}

[id=\$canvas2]{
  margin:10px;
  background:lightgray;
  border-radius:4px;
  width:200px;
  height:100px;
}

body{
  display:flex;
}

[id=\$log]{
  font-size:12px;
}
<div id = "$canvas"></div>
<div id = "$canvas2"></div>
<div id = "$log"></div>

Привет! Я только что добавил некоторую информацию к этому вопросу. @ Александр Ненашев

lai9fox 07.07.2023 16:00

@lai9fox сделан в подходящем для всех событий указателя

Alexander Nenashev 07.07.2023 17:00

Спасибо за ваш ответ @Alexander Nenashev Нажмите F12 на этой странице, чтобы открыть консоль, и введите document.body.style = "scale: 0.8;width: 125%;transform-origin: 0 0;"; в консоли. Затем нажмите recent inbox messages в верхней части страницы, что, вероятно, вы и видите. Это краткий пример того, как я модифицировал элемент body, к сожалению, вычисление getBoundingClientRect также подвержено ошибкам.

lai9fox 07.07.2023 17:05

@lai9fox обновил ответ с помощью исправления обезьяны, поэтому вам не нужно устанавливать обработчики событий вручную.

Alexander Nenashev 07.07.2023 17:07

@lai9fox lai9fox, если вам нравится мой ответ, вы можете принять его (флажок) и проголосовать (стрелка вверх). таким образом, я буду вознагражден и мотивирован, чтобы помогать вам дальше...

Alexander Nenashev 07.07.2023 17:08

@lai9fox обновил окончательное решение, выглядит потрясающе

Alexander Nenashev 07.07.2023 17:15

Эта или подобные задачи могут быть описаны как модификация метода, которая представляет собой манипулирование управлением функцией/методом и/или потоком данных.

Такое поведение причины может быть реализовано на более высоком уровне абстракции, например. around, before, after, afterThrowing и afterFinally модификаторы.

ОП ищет модификацию, которая происходит до вызова исходного метода.

В приведенном ниже коде показана исходная обработка события click обработчиком handleClickEvent и обработанная обработка события модифицированной версией той же самой функции handleClickEvent. Последний использует также предоставленную возможную реализацию модификатора before.

Затем все решение сводится к 3 небольшим функциям: реализации модификатора before, фактическому/оригинальному обработчику кликов и дополнительной функции, которая манипулирует данными события...

function handleClickEvent(evt) {
  const mouseCoords = {
    x: evt.clientX,
    y: evt.clientY,
  };
  console.info({ mouseCoords });
}
function manipulateEventData([evt]) {
  // - manipulate the original event's data.
  Object.defineProperties(evt, {
    clientX: {
      value: evt.clientX + 1000,
    },
    clientY: {
      value: evt.clientY + 1000,
    },
  });
}

document
  .querySelector(`[data-original-handler]`)
  .addEventListener('click', handleClickEvent);

document
  .querySelector(`[data-modified-handler]`)
  .addEventListener(
    'click',
    modifyBefore(handleClickEvent, manipulateEventData)
  );
<button data-original-handler>original click handler</button>
<button data-modified-handler>modified click handler</button>

<script>
function modifyBefore(proceed, handler, target = null) {
  // - `target` equals the `this` context of the to be modified method.
  return (
    typeof proceed === 'function' &&
    typeof handler === 'function' &&

    function beforeType(...args) {
      // - the target/context of the initial modifier/modification time
      //   still can be overruled by a handler's apply/call time context.
      const context = this ?? target;

      // - invocation of the `before` handler.
      handler.call(context, args);

      // - proceed with invoking the original function/method.
      return proceed.apply(context, args);
    }
  ) || proceed;
}
</script>

@ lai9fox ... ОП может проверить приведенное выше, поздно предоставленное, но очень удобное решение проблемы ОП.

Peter Seliger 07.07.2023 19:01

Другие вопросы по теме