skip to Main Content
<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.

2

Answers


  1. You will need to make a copy of the event, since the clientX and clientY properties are read-only.

    Intercept the event in another function, modifiy the event, and send it into the original handler.

    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.log(`Point: ${JSON.stringify(point)}`);
    }
    <button onclick="myFunction(event)">CLICK</button>
    <button id="myButton">CLICK (MODIFIED)</button>

    If you want to properly clone the event, you can try:

    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.log(`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>
    Login or Signup to reply.
  2. A generic solution with Proxy to modify an event.
    You can create a global getter object to fix your event values, in the snippet there’s a correction to make event.clientX/Y to have the pointer coordinates relative to the target element for all elements inside a container element of choice:

    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 _add.call(this, name, event, ...args);
        });
    };
    
    
    // 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);
      }
    };
    
    'mousemove mousedown mouseup'.split(' ').forEach(name => $canvas.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:20px;
      background:lightgray;
      border-radius:4px;
      width:200px;
      height:100px;
    }
    <div id="$canvas"></div>
    <div id="$log"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search