Issues triggering a command upon display of newly created html element

I’m trying to build an extension and under activate, I create a new div. I want to add a command to trigger when this div appears (if the display != none). I’ve tried adding the div’s selector name to the selector field in app.commands.addKeyBinding but that didn’t work. It did work when I tested with selectors that I didn’t create (those that were already in JupyterLab). I’m assuming this means that selector doesn’t work for created HTML elements. How can I add a command to trigger only when my div appears?

Shortcut selectors should work fine for pragmatically created elements which are in the DOM. Do you have a minimal reproducible example? For example, something that could demonstrate the problem in GitHub - jupyterlab/jupyterlab-plugin-playground: A dynamic extension loader for JupyterLab (on Binder)?

I don’t have a minimum reproducible example though I can make one. First, I just want to make sure I’m not misunderstanding on what to put in the selector in addKeyBinding and how it works. In more detail, I have a:

namespace CommandIDs {
    export const testCommand = 'command:test';
}

const extension: JupyterFrontEndPlugin<void> = {
    id: 'jupyterlab_apod2:plugin',
    autoStart: true,
    requires: [ICommandPalette, IEditorTracker],
    activate: async (app: JupyterFrontEnd, palette: ICommandPalette, editorTracker: IEditorTracker) => {
      console.log(' JupyterLab extension jupyterlab_apod2 is activated!');

      let body = document.getElementsByTagName("body")[0];
      let completerWidget = document.createElement('div');
      completerWidget.className = 'jp-Completing';
      body.appendChild(completerWidget);

      app.commands.addCommand(CommandIDs.testCommand, {
        execute: () => {
          console.log("test command has been triggered");
        },
      });

      app.commands.addKeyBinding({
        command: CommandIDs.testCommand, // id of the command to execute when the binding is matched
        keys: ['Accel L'], // default key sequence for the key binding
        selector: ".jp-Completing", // CSS Selector for the key binding
      });

I open a file editor and I press Command L. I expect the statement “test command has been triggered” to be printed on the console. Is that correct?

Ok, I see. Yes, it should work like this for elements which receive keydown events. No, it will not work for your specific example because a plain div does not listen to keyboard events by default. So to make this example work you can either switch to something which listens to keydown by default, like textarea:

import { JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application';
import { ICommandPalette } from '@jupyterlab/apputils';

namespace CommandIDs {
    export const testCommand = 'command:test';
}

const plugin: JupyterFrontEndPlugin<void> = {
  id: 'hello-world:plugin',
  autoStart: true,
  requires: [ICommandPalette],
  activate: (app: JupyterFrontEnd, palette: ICommandPalette) => {
      let body = document.getElementsByTagName("body")[0];
      let completerWidget = document.createElement('textarea');
      completerWidget.className = 'jp-Completing';
      completerWidget.style.position = 'absolute';
      completerWidget.style.top = '0px';
      completerWidget.style.right = '0px';
      completerWidget.style.zIndex = '10000';
      completerWidget.style.width = '100px';
      completerWidget.style.height = '100px';
      completerWidget.style.background = 'white';
      body.appendChild(completerWidget);
      if (!app.commands.hasCommand(CommandIDs.testCommand)) {
          app.commands.addCommand(CommandIDs.testCommand, {
            execute: () => {
              console.log("test command has been triggered");
            },
          });
      }

      app.commands.addKeyBinding({
        command: CommandIDs.testCommand,
        keys: ['Accel L'],
        selector: ".jp-Completing",
      });

  },
};

export default plugin;

Or allow the div to get focused so that it can be a keydown target by setting a tabIndex (to zero or negative).

import { JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application';
import { ICommandPalette } from '@jupyterlab/apputils';

namespace CommandIDs {
    export const testCommand = 'command:test';
}

const plugin: JupyterFrontEndPlugin<void> = {
  id: 'hello-world:plugin',
  autoStart: true,
  requires: [ICommandPalette],
  activate: (app: JupyterFrontEnd, palette: ICommandPalette) => {
      let body = document.getElementsByTagName("body")[0];
      let completerWidget = document.createElement('textarea');
      completerWidget.className = 'jp-Completing';
      completerWidget.style.position = 'absolute';
      completerWidget.style.top = '0px';
      completerWidget.style.right = '0px';
      completerWidget.style.zIndex = '10000';
      completerWidget.style.width = '100px';
      completerWidget.style.height = '100px';
      completerWidget.style.background = 'white';
      completerWidget.tabIndex = 0;
      body.appendChild(completerWidget);
      if (!app.commands.hasCommand(CommandIDs.testCommand)) {
          app.commands.addCommand(CommandIDs.testCommand, {
            execute: () => {
              console.log("test command has been triggered");
            },
          });
      }

      app.commands.addKeyBinding({
        command: CommandIDs.testCommand,
        keys: ['Accel L'],
        selector: ".jp-Completing",
      });

  },
};

export default plugin;