Accessible Clickable Area

by _sir

HTML

<div class="actionable" data-click-area-expanded>
  <p>
    So much content
    that it's not funny
  </p>
  <p>
    <a href="#" class="learnMore">Learn More</a>
  </p>
  <label for="buttonBlue" class="sr-only">View Details for Check 844</label>
  <button id="buttonBlue" class="viewDetail" data-click-area-target="true">
    &rarr;
  </button>
</div>

<div class="actionable" data-click-area-expanded>
  <p>
    So much content
    that it's not funny
  </p>
  <button class="info learnMore">
    ℹ️
  </button>
  <label for="buttonRed" class="sr-only">View Details for Check 123</label>
  <button id="buttonRed" class="viewDetail" data-click-area-target="true">
    <span>&raquo;</span>
  </button>
</div>

<div id="container">
</div>

CSS

div.actionable {
  margin: 1em;
  padding: 1em;
  border: solid gray;
  border-width: 1px 0;
  height: 4em;
  position: relative;
}

.actionable button.viewDetail {
  background: steelblue;
  color: white;
  font-weight: bold;
  font-size: 2rem;
  position: absolute;
  border: none;
  height: 100%;
  top: 0;
  right: 0;
}

button.info {
  border: none;
  background: none;
}

#buttonBlue {
  background: steelblue;
  color: white;
}

#buttonRed {
  background: maroon;
  color: white;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: none;
}

React

function ExpandedClickableArea(props) {
  const clickableElemTypes = ['a', 'button', 'input'];
  const refExpandedArea = useRef();

  function handleClick(e) {
    const actionTargets = [
      ...refExpandedArea.current.querySelectorAll('[data-expand-click-area]')
    ];
    if (clickableElems.length !== 1) {
      throw new Error(
        `Expected one clickable element but found ${clickableElems.length}`
      );
    }
    const [actionTarget] = actionTargets0;
    
    const clickableElements = [...refExpandedArea.current.querySelectorAll('a,button,input')];
    const originalIsClickable = clickableElements.some((el) => el === e.target || el.contains(e.target));

    if (clickableElem !== e.target && !originalIsClickable) {
      clickableElem.click();
    }
  }

  return (
    <div
      ref={refExpandedArea}
      className={`${props.className} ${styles.expandedClickableArea}`}
      tabIndex="0"
      onClick={handleClick}
    >
      {props.children}
    </div>
  );
};

ReactDOM.render(
	<ExpandedClickableArea>
	  
	</ExpandedClickableArea>,
  document.getElementById('container')
);

document.getElementById('buttonBlue').addEventListener('click', () => console.log('We clicked the blue button!'));
document.getElementById('buttonRed').addEventListener('click', () => console.log('We clicked the red button!'));

[...document.querySelectorAll('.learnMore')].forEach(el => el.addEventListener('click', () => console.log('Learn more!')));

const clickableElemTypes = ['a', 'button', 'input'];


[...document.querySelectorAll('[data-click-area-expanded]')].forEach((container) => {
  container.addEventListener('click', (e) => {
    const targets = [...container.querySelectorAll('[data-click-area-target]')];
    if (targets.length !== 1) {
      throw new Error('Could not find single click target');
    }
    const [actionTarget] = targets;
    const clickables = [...container.querySelectorAll('a,button,input')];
    const inClickable = clickables.some((el) =>...