0

I am trying to get some references on how to do a workaround.

I don't have access to the codebase I am trying to edit. It is a software with a built in functionality that I need to re-write and I can't pass a ref to the components where I need to detect the event.

I need to detect a click within a component that contains the id gatsby-focus-wrapper. But within this component there are other components with the class component-wrapper.

TL;DR so the click should be within class gatsby-focus-wrapper but outside component-wrapper.

I was detecting outside events with this hook:

export const useOnClickOutside = (ref: any, handler: any, canClickOutside = true) => {
  useEffect(() => {
    const listener = (event: any) => {
      // Do nothing if clicking ref's element or descendent elements
      if (!ref.current || ref.current.contains(event.target)) {
        return;
      }

      handler(event);
    };

    const onEscape = (event: any) => {
      if (event.keyCode === 27) {
        handler(event);
      }
    };

    if (canClickOutside) {
      document.addEventListener('keydown', onEscape);
      document.addEventListener('mousedown', listener);
      document.addEventListener('touchstart', listener);

      return () => {
        document.removeEventListener('keydown', onEscape);
        document.removeEventListener('mousedown', listener);
        document.removeEventListener('touchstart', listener);
      };
    }

    return undefined;
  },

  [ref, handler, canClickOutside]
  );
};

But now I have another challenge.

Any ideas?

Non
  • 8,409
  • 20
  • 71
  • 123

1 Answers1

0

You can use Element.closest() to check if the clicked element (.target) is a child of .gatsby-focus-wrapper, and not a child of .component-wrapper:

document.addEventListener('click', e => {
  const notInRed = !e.target.closest('.component-wrapper')
  const inBlue = !!e.target.closest('.gatsby-focus-wrapper')

  console.log({
    notInRed,
    inBlue
  })
})
.gatsby-focus-wrapper {
  width: 50vw;
  height: 50vh;
  background: blue;
}

.component-wrapper {
  width: 25vw;
  height: 25vh;
  background: red;
}
<div class="gatsby-focus-wrapper">
  <div class="component-wrapper"></div>
</div>

Usage with React:

const {
  useRef,
  useEffect
} = React;

const useOnClickOutside = (handler, container, without) => {
  useEffect(() => {
      const listener = (event) => {
        // check that it's inside the container, but not inside the without element
        if (!event.target.closest(container) || event.target.closest(without)) {
        return;
        }

        handler(event);
      };

      const onEscape = (event) => {
        if (event.keyCode === 27) {
          listener(event);
        }
      };

      document.addEventListener('keydown', onEscape);
      document.addEventListener('mousedown', listener);
      document.addEventListener('touchstart', listener);

      return () => {
        document.removeEventListener('keydown', onEscape);
        document.removeEventListener('mousedown', listener);
        document.removeEventListener('touchstart', listener);
      };
    },

    [handler, container, without]
  );
};


const handler = () => console.log('handled')

const Demo = () => { 
  useOnClickOutside(handler, '#gatsby-focus-wrapper', '.component-wrapper');

  return (
    <div id="gatsby-focus-wrapper">
      <div className="component-wrapper"></div>
    </div>
  );
};

ReactDOM.render(
  <Demo />,
  root
);
#gatsby-focus-wrapper {
  width: 50vw;
  height: 50vh;
  background: blue;
}

.component-wrapper {
  width: 25vw;
  height: 25vh;
  background: red;
}
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209