javascriptfunctionproxydom-eventsmodifier

How to modify the event object before the DOM event callback function is executed


<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>

I might add click events to the button by the two methods above.

I want to modify some properties of the event object that myFunction will receive before it executes.

For example, in the click event, the event has clientX and clientY properties, and assuming the original values are 100 and 100, I want them to be 150 and 150 (how they change is determined by certain conditions) and the other property values to be the same as the original values.

Is there any way to achieve this?


Additional Information

I needed the modified event because for some reason I added the scale, width style attributes to the body element, which made the position-related coordinate points calculate incorrectly.

Some events related to coordinate points, such as click, drag, are not behaving properly.

Basically, every point-related event object has to be fixed, most notably MouseEvent, PointerEvent.

According to @Mr. Polywhirl, I need to write a function to handle the event object first, and then call the actual callback in the handling function, which is more replicated.

Is it possible to achieve a senseless modification by modifying the addEventListener on the prototype?

If it works, how would a binding like <button onclick="myFunction(event)">CLICK</button> achieve the same event modification?

Try this

Press F12 on this page to open the console, and type document.body.style="scale: 0.8;width: 125%;transform-origin: 0 0;" in the console. Then click on the recent inbox messages at the top of the page, which is probably what you're seeing.

What I need to achieve

I don't want to make any changes to the callback function myFunction

A hypothetical case:

// Original call
<script>
  button.addEventListener("click", myFunction);
  function myFunction(event) {
    // event.clientX maybe 100
  }
</script>


// After applying some magic code
<script>

  // some magic code
  // ...

  button.addEventListener("click", myFunction);
  function myFunction(event) {
    // event.clientX is the original value of 100 and then changed according to some conditions
    // such as expanding it by 1.5 times, now it is 150 (This is what the magic code needs to accomplish)
  }
</script>

Solution

  • This or similar tasks could be described as method modification which is the manipulation of a function's/method's control- and/or data flow.

    Such behavior of cause can be implemented at a higher abstraction level as e.g. around, before, after, afterThrowing and afterFinally modifiers.

    The OP is looking for a modification which happens before the invocation of an original method.

    The following provided code shows the original event handling of a click event by a handleClickEvent handler and the manipulated event handling by a modified version of the very same handleClickEvent function. The latter utilizes an also provided possible implementation of a before modifier.

    The entire solution then boils down to 3 small functions, the before modifier implementation, the actual/original click handler and an additional function which does manipulate the event data ...

    function handleClickEvent(evt) {
      const mouseCoords = {
        x: evt.clientX,
        y: evt.clientY,
      };
      console.log({ 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>