react-onclickoutside icon indicating copy to clipboard operation
react-onclickoutside copied to clipboard

Shadow DOM support?

Open Jarred-Sumner opened this issue 5 years ago • 7 comments

When using react-onclickoutside from inside a Shadow DOM, the events propagate through to the DOM node containing the shadow DOM and do not descend into the shadow DOM.

This means react-onclickoutside is still useful! It essentially becomes react-onclickoutsideshadowdom (once you add .ignore-react-onclickoutside to the root of the shadow DOM). However, it'd be even better if it knew to listen for events within the Shadow DOM, so that it knows to ignore clicks inside the "real" target.

Concrete example: I have a dropdown inside a shadow DOM. I want that dropdown to only disappear when clicking outside of the dropdown (versus anywhere on the page), and the default behavior right now is handleClickOutside gets called on any click. So, I added ignore-react-onclickoutside to the shadow DOM root, and now it gets called only when I click outside of the shadow DOM.

Maybe one potential approach here would be adding a config option for rootNodes which defaults to [document], and then in the following places, it loops through rootNodes instead of document directly? Then, when using from inside a shadow DOM, we would add the root node of the shadow DOM to the list of rootNodes?

  • https://github.com/Pomax/react-onclickoutside/blob/master/src/index.js#L170
  • https://github.com/Pomax/react-onclickoutside/blob/master/src/index.js#L188

Jarred-Sumner avatar Jul 20 '18 01:07 Jarred-Sumner

Are you just adding the class ignore-react-onclickoutside to the root div of the shadow dom? I'm doing the following but the click is still closing the calendar.

const template = `
    <style>
        @import "${ this.getAttribute('pathToStyles') }";
        #root {
            font-size: 1rem;
            display: block;
        }
    </style>
    <div id="root" class='ignore-react-onclickoutside'></div>`;
this.shadow.innerHTML = template;

conor909 avatar Oct 19 '18 11:10 conor909

Adding the ability to specify a custom root so that the HOC stops once it hits that makes a lot of sense. Would you be up for writing a PR for that, @Jarred-Sumner?

Pomax avatar Oct 19 '18 18:10 Pomax

I've run into this same issue, when using react-datepicker within a shadowdom, click events within the date picker are hiding the date picker :(

Is there a simple solution for this ?

cameronbraid avatar May 23 '19 14:05 cameronbraid

Could a solution be to walk up out of a shadow root into the containing document, so change findHighest to

function findHighest(current, componentNode, ignoreClass) {
  if (current === componentNode) {
    return true;
  } // If source=local then this event came from 'somewhere'
  // inside and should be ignored. We could handle this with
  // a layered approach, too, but that requires going back to
  // thinking in terms of Dom node nesting, running counter
  // to React's 'you shouldn't care about the DOM' philosophy.


  while (current.parentNode) {
    if (isNodeFound(current, componentNode, ignoreClass)) {
      return true;
    }

    // if we are at a shadow root, continue up via the host document
    if (!current.parentNode && current.host) {
      current = current.host
    }
    else {
      current = current.parentNode;
    }

  }

from local testing this seems to work

cameronbraid avatar May 23 '19 14:05 cameronbraid

actually, I think it can be simplified to

current = current.parentNode || current.host;

cameronbraid avatar May 23 '19 14:05 cameronbraid

Actaully, I was wrong, I had broken something.

What appears to be happening is that the mousedown listener is registered on the host document, and therefore the event target is the element that hosts the shadow root, not the element that is contained within the react component

cameronbraid avatar May 23 '19 14:05 cameronbraid

fyi https://github.com/Pomax/react-onclickoutside/pull/323

cameronbraid avatar May 24 '19 02:05 cameronbraid