Search code examples
javascripthtmlxpathgoogle-chrome-extension

cannot register click event on a span button from content script of the chrome extension | Google meet | chrome extension


I'm trying to build a chrome extension that records all the captions which are generated by Google and at the end of the meeting it produces a transcript file. but to do that I need to first enable the captions.

to turn on the caption we need to click on the more option button. which opens a menu and we can click on captions and select the appropriate language.turn on caption 2 as you can see some browsers don't have that turn on captions button at the footer.

now to achieve this it involves multiple stages like clicking on more options first and then clicking on captions and then selecting a language. I was able to open the more options menu with the following code:

    const moreIconButton = xpath(
      `(//span[@class='NlWrkb snByac'])[2]`,
      document
    );
    moreIconButton.click();

after execution of this code, it opens the menu. now the next step will be clicking on the captions button in the menu. I'm trying to do that with the following code:

      const captionsButton = xpath(`(//span[@class='z80M1'])[4]`, document);
      captionsButton.click();
     

this step is not working. I tried consoling the dom returns by xpath and it is correct.

<span jsslot="" class="z80M1" jsaction="click:o6ZaF(preventDefault=true); mousedown:lAhnzb; mouseup:Osgxgf; mouseenter:SKyDAe; mouseleave:xq3APb;touchstart:jJiBRc; touchmove:kZeBdd; touchend:VfAz8(preventMouseEvents=true)" jsname="j7LFlb" data-disabled-tooltip="Live captions are temporarily unavailable." aria-label="Captions Off" role="menuitem" tabindex="-1"><div class="aBBjbd MbhUzd" jsname="ksKsZd"></div><div class="PCdOIb Ce1Y1c" aria-hidden="true"><span class="DPvwYc SX67K" aria-hidden="true"><svg width="24" height="24" viewBox="0 0 24 24" focusable="false" class="Hdh4hc cIGbvc NMm5M"><path d="M19 4H5a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H5V6h14v12z"></path><path d="M7 15h3c.55 0 1-.45 1-1v-1H9.5v.5h-2v-3h2v.5H11v-1c0-.55-.45-1-1-1H7c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1zm7 0h3c.55 0 1-.45 1-1v-1h-1.5v.5h-2v-3h2v.5H18v-1c0-.55-.45-1-1-1h-3c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1z"></path></svg></span></div><div class="uyYuVb oJeWuf" jscontroller="ljY3Ib" jsaction="JIbuQc:tNG7Z;" jsname="ARMOIc"><div class="jO7h3c"><div class="r5dJdc">Captions<div class="yiytvb">Off</div></div></div></div></span>

but it doesn't register the click event on it.

I tried all the possible combinations of xpath and almost gone through all the similar StackOverflow answers. but couldn't find the exact solution.

edit1: I also tried doing all the steps manually on the developer console of the chrome.

note:

  1. I'm getting all xpaths by using letxpath extension.
  2. xpath is a function that evaluates the given XPath and returns a DOM

thanks.


Solution

  • Google Meet uses JsAction for event handling that allows decoupling the DOM nodes where the code action occurs.

    I recommend you review this library in depth to achieve what you need. https://opensource.google/projects/jsaction

    And this example can help you a little more

    http://jsfiddle.net/880m0tpd/4/

    const eventContract = new jsaction.EventContract();
    
    // Events will be handled for all elements under this container.
    eventContract.addContainer(window.document.documentElement);
    
    // Register the event types handled by JsAction.
    eventContract.addEvent('click');
    
    // This code will be executed after the dispatcher loads, which is done 10 seconds
    // after page load to simulate late loading the dispatcher. When the dispatcher loads,
    // this code will replay any event that was captured before the dispatcher was
    // loaded.
    const lateLoadedCode = `
    // This is the actual event handler code.
    function handleEvent() {
      alert('event handled!');
    }
    
    // This code replays the queued events. Applications can define custom replay
    // strategies.
    function replayEvents(events, jsActionDispatcher) {
      while (events.length) {
        jsActionDispatcher.dispatch(events.shift());
      }
    }
    
    // Initialize the dispatcher, register the handlers, and then replay the queued events.
    const dispatcher = new jsaction.Dispatcher();
    eventContract.dispatchTo(dispatcher.dispatch.bind(dispatcher));
    dispatcher.registerHandlers(
        'button',
        null,
        { 'handleEvent': handleEvent });
    dispatcher.setEventReplayer(replayEvents);
    `;
    
    // Load the dispatcher code 10 seconds after page load to simulate late loading
    // the dispatcher while the contract queues events.
    window.setTimeout(function() {
      alert('loading dispatcher');
      var script = document.createElement('script');
      script.src = 'https://www.gstatic.com/jsaction/dispatcher.js';
      script.async = true;
      script.onload = () => eval(lateLoadedCode);
      document.getElementById('contract').parentNode.appendChild(script);
    }, 10000);
    <script id="contract" src="https://www.gstatic.com/jsaction/contract.js"></script>
    
    <button jsaction="button.handleEvent">
      click here to capture events
    </button>
    
    <hr>
    <div>
    Click on the button above to capture events. Even though the dispatcher has not loaded yet, these events will be queued until the dispatcher does load. In 10 seconds, the event dispather will load and automatically replay the events.
    </div>