yomichan icon indicating copy to clipboard operation
yomichan copied to clipboard

Programmatically trigger Yomichan popups with JavaScript

Open KamWithK opened this issue 2 years ago • 10 comments

Hey Yomichan team,

I was wondering whether there is a way to manually trigger a Yomichan lookup/popup over a word? Couldn't find any GitHub issues or anything online about this

Thanks for any help :)

KamWithK avatar Nov 06 '22 10:11 KamWithK

As it stands currently, probably not. Synthetic events may or may not work, but since scanning is entirely customizable, there's no way to figure out what event settings would be required.

toasted-nutbread avatar Nov 06 '22 13:11 toasted-nutbread

I see, makes sense

Would you be able to give any advice on how to form a synthetic event for it? Or would it not be worth the effort?

KamWithK avatar Nov 06 '22 13:11 KamWithK

someElement.dispatchEvent(new MouseEvent('mousemove', {
  screenX: SOME_VALUE, // probably not needed
  screenY: SOME_VALUE, // probably not needed
  clientX: SOME_VALUE,
  clientY: SOME_VALUE,
  ctrlKey: false,
  shiftKey: true,
  altKey: false,
  metaKey: false,
  button: 0,
  buttons: 0,
  relatedTarget: null,
}));

This is assuming that the user's scanning configuration is to use the shift key + mouse motion to scan, which is not guaranteed. Ultimately, this would probably be something related to a Yomichan API, which has been brought up in #1649 previously, but hasn't been implemented.

toasted-nutbread avatar Nov 07 '22 02:11 toasted-nutbread

Hmm I tried to do that but I can't exactly get the popup to appear (shift is my popup key)

For a nice and easy example I used a word from your message just now to test:

const test = document.querySelector(".pl-en")
const bound = test.getBoundingClientRect()

test.dispatchEvent(new MouseEvent('mousemove', {
//   screenX: bound.x + bound.left,
//   screenY: bound.y + bound.top,
  clientX: bound.left,
  clientY: bound.top,
  ctrlKey: false,
  shiftKey: true,
  altKey: false,
  metaKey: false,
  button: 0,
  buttons: 0,
  relatedTarget: null,
}))

Anything else you think I could quickly try? Or maybe this isn't really possible rn?

KamWithK avatar Nov 07 '22 07:11 KamWithK

You can try running this script on this page:

(() => {
  const node = document.querySelector('.comment-body>h3');
  const bound = node.getBoundingClientRect()
  const event = new MouseEvent('mousemove', {
    clientX: bound.left + 1, // These coordinates account for padding and such
    clientY: bound.bottom - 10,
    ctrlKey: false,
    shiftKey: true,
    altKey: false,
    metaKey: false,
    button: 0,
    buttons: 0,
    relatedTarget: null,
  });
  node.dispatchEvent(event);
})();

It should detect this text below (if it's on-screen):

読む

As you might be able to tell, the coordinates are fairly picky about what it considers to be hovering over the text.

Also, this seems to only work in Firefox.

toasted-nutbread avatar Nov 07 '22 22:11 toasted-nutbread

Hmm interesting bc I'm on Firefox but running your code does nothing for me (no popup appears on the screen) I wonder why it works for you and not me

How did you find the padding values to use there btw? Maybe they can change between computers and so need to be found within the code?

KamWithK avatar Nov 08 '22 00:11 KamWithK

It's just kind of a guess based on what I was seeing in the inspector. Should be fairly similar across devices at least, not sure why it would work for me but not for you. Seems to work the same if I don't use the console and embed the code in a <script> tag on the page as well.

Update: it also seems to work in Chrome if I add bubbles: true to the event init options.

demo

toasted-nutbread avatar Nov 09 '22 00:11 toasted-nutbread

That's even more interesting because I just tried Chrome with bubbles set to true and that had no effect either I can with mouse mouse activate that bubble but I can't with the console using your code

KamWithK avatar Nov 09 '22 00:11 KamWithK

Full standalone HTML page example below. Again, this relies on the scanning key being "shift".

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Scan Test</title>
</head>
<body>

<h3 id="text">読む</h3>

<button id="test">Test</button>

<script>
document.querySelector('#test').addEventListener('click', () => {
  const node = document.querySelector('#text');
  const bound = node.getBoundingClientRect()
  const event = new MouseEvent('mousemove', {
    clientX: bound.left + 1, // These coordinates account for padding and such
    clientY: (bound.top + bound.bottom) * 0.5,
    ctrlKey: false,
    shiftKey: true,
    altKey: false,
    metaKey: false,
    button: 0,
    buttons: 0,
    relatedTarget: null,
    bubbles: true,
  });
  node.dispatchEvent(event);
}, 1000);
</script>

</body></html>

Demo: (left - Firefox, right - Chrome) demo2

toasted-nutbread avatar Nov 09 '22 03:11 toasted-nutbread

Just gave that a go but nothing seems to happen at all (even tho I can shift and hover over it and it'll pop up) I don't think it's something to do with the screen size because even if i resize it the amount of padding/margin shouldn't really change much and you've taken the actual bound top/bottom/left values for it anyways

KamWithK avatar Nov 10 '22 03:11 KamWithK