vanowm
vanowm

Reputation: 10221

Change event before it propagates to next listener?

Is there a way change an event and let it propagate further with the change?

For example how can I "force" press shift when any key is pressed?

window.focus();

window.addEventListener("keypress", e =>
{
  //force shift key doesn't work
  e.shiftKey = true;
})

window.addEventListener("keypress", e =>
{
  console.log("shift", e.shiftKey);
});

(this is just an example, I'm looking for a general solution that would work with any type of event)

Upvotes: 4

Views: 556

Answers (2)

The Bomb Squad
The Bomb Squad

Reputation: 4337

You can't "change" a KeyboardEvent. It's read-only.

It took a while, but apparently it IS the same object that gets passed to all the event listeners.. I messed with the get properties of Event and KeyboardEvent and the edited event passes smoothly :D

window.focus();

function control(event){
  const eventMap=new WeakMap()
  const getMap=(instance,key)=>{
    let map=eventMap.get(instance)
    if(!map){
      map={[key]:{value:null,valueSet:false}}
      eventMap.set(instance,map)
    }
    if(!map[key]){map[key]={value:null,valueSet:false}}
    return map[key] //specific to an event instance AND key
  }
  if(typeof event=='function'){event=event.prototype}
  let properties=Object.getOwnPropertyDescriptors(event)
  Object.keys(properties)
  .filter(key=>
    typeof(properties[key].get)=='function'
  )
  .forEach(key=>{
    const original=properties[key].get
    function set(something){
      let map=getMap(this,key)
      map.value=something; map.valueSet=true
    }
    function get(){
      let {value,valueSet}=getMap(this,key)
      return valueSet? value: original.call(this)
    }
    properties[key]={...properties[key],get,set}
  })
  Object.defineProperties(event,properties)
}
//for any event class given to it, this function makes its prototype functions malleable
control(KeyboardEvent); control(Event)
//not all but A LOT of the prototypes including shiftKey fall under these 2 classes

//eventListener that I assume you control and add first
window.addEventListener("keypress", e =>
{
  e.shiftKey=!e.shiftKey //your edit to the event(switches the shiftKey event locally and not globally)
})

//event listener that might be in the environment that you didn't make
window.addEventListener("keypress", e =>
{
  console.log("shift", e.shiftKey); //false when u use shift, true when u don't use shift now >:D
});

console.log("press an unshifted letter in this area")

@vanoworm, for your electron app.. here's a working example of an event edit working smoothly

EDIT I saw your response

basically it changes shiftKey property globally for all future events, not only for current one.

Thank you for telling me.. I just fixed that and also.. about control.js, it can just be a string on your backend since it's meant to execute on the browser ;-; my bad

index.js

var control=require('./control.js')
const {BrowserWindow,app} = require('electron')
app.on('ready',()=>{
  const win = new BrowserWindow({ width: 800, height: 600 })
  let script=control+'\ncontrol(Event); control(KeyboardEvent)\n'
  +'window.addEventListener("keypress",e=>e.shiftKey=true)\n'
  +'window.addEventListener("keypress",e=>console.log(e.shiftKey))\n'
  +'console.log("press an unshifted letter in this browser")'
  win.webContents.executeJavaScript(script)
  win.loadURL('https://github.com')
})

control.js

module.exports=`
function control(event){
  const eventMap=new WeakMap()
  const getMap=(instance,key)=>{
    let map=eventMap.get(instance)
    if(!map){
      map={[key]:{value:null,valueSet:false}}
      eventMap.set(instance,map)
    }
    if(!map[key]){map[key]={value:null,valueSet:false}}
    return map[key] //specific to an event instance AND key
  }
  if(typeof event=='function'){event=event.prototype}
  let properties=Object.getOwnPropertyDescriptors(event)
  Object.keys(properties)
  .filter(key=>
    typeof(properties[key].get)=='function'
  )
  .forEach(key=>{
    const original=properties[key].get
    function set(something){
      let map=getMap(this,key)
      map.value=something; map.valueSet=true
    }
    function get(){
      let {value,valueSet}=getMap(this,key)
      return valueSet? value: original.call(this)
    }
    properties[key]={...properties[key],get,set}
  })
  Object.defineProperties(event,properties)
}
`

Upvotes: 1

CertainPerformance
CertainPerformance

Reputation: 370999

Use strict mode to see why your code isn't working:

'use strict';
window.focus();

window.addEventListener("keypress", e =>
{
  //force shift key doesn't work
  e.shiftKey = true;
})

window.addEventListener("keypress", e =>
{
  console.log("shift", e.shiftKey);
});

The event that the browser passes to addEventListener (regardless of propagation status) can't have certain intrinsic properties changed, including shiftKey. While you could assign to a different non-reserved property of the event, a better approach I think would be to use a WeakMap that maps the event to the new propert(ies) you want.

'use strict';
const customPropertiesByEvent = new WeakMap();
window.focus();

window.addEventListener("keypress", e =>
{
  customPropertiesByEvent.set(e, { shift: true });
})

window.addEventListener("keypress", e =>
{
  console.log("shift", customPropertiesByEvent.get(e).shift);
});

Upvotes: 1

Related Questions