Pomax / react-onclickoutside

An onClickOutside wrapper for React components
MIT License
1.83k stars 187 forks source link

Shadow DOM support? #285

Open Jarred-Sumner opened 6 years ago

Jarred-Sumner commented 6 years ago

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?

conor909 commented 6 years ago

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;
Pomax commented 6 years ago

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?

cameronbraid commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

actually, I think it can be simplified to

current = current.parentNode || current.host;
cameronbraid commented 5 years ago

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 commented 5 years ago

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