Orlyyn
Orlyyn

Reputation: 2616

How to verify if an element's ancestor contains a certain type?

In my app, I would like to find out if the element (or element's parent, or grand parents, or grand grand parents) I'm clicking on has a certain type (such as button), in order to trigger a side effect if it hasn't the type I'm looking for.

For example, if I click on an element svg that is embedded in a span, which is embedded in a button, the checker function would return "true", since an ancestor of the svg element is of type button.

So far, I have the following check in the isParentFocusable function:

private isFocusable(el: HTMLElement) {
  if (!el) {
    return false;
  }
  if (el.getAttribute('focusable')) {
    return true;
  }

  let types = ['input', 'textarea', 'button', 'select', 'a', 'menuitem'];
  return (
    this.verifyRoleOrTagName(el, types) ||
    this.isParentFocusable(el, types)
  );
}

private verifyRoleOrTagName = (element: HTMLElement, types: string[]) => {
  return (
    types.includes(element.tagName.toLocaleLowerCase()) ||
    types.includes(element.getAttribute('role') || '')
  );
};

private isParentFocusable(el: HTMLElement, types: string[]) {
  let currentElement: HTMLElement | null | undefined = el;
  // The upmost level to check for the parent is set to 3;
  // we don't want to check all the parents up to the body tag
  const MAX_PARENT_LEVEL = 3;
  for (let i = 0; i < MAX_PARENT_LEVEL && currentElement; i++) {
    currentElement = currentElement.parentElement;
    if (currentElement && this.verifyRoleOrTagName(currentElement, types)) {
      return true;
    }
  }

  return false;
}

Is there an elegant way or a known method to check for the clicked element to be embedded in a tag of a certain type? (Here, the types are listed in the types array).

Upvotes: 1

Views: 440

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1075309

You can use the closest method to do that. It finds the closest element that matches a CSS selector, starting with the element you call it on, then going to the parent, etc.

const button = theElement.closest("button");

Here's an example:

function clickHandler(event) {
    const button = event.target.closest("button");
    console.log(`Found button? ${button ? "Yes" : "No"}`);
}

for (const span of document.querySelectorAll("span")) {
    span.addEventListener("click", clickHandler);
}
<button type="button">
    <span>Click me</span>
</button>
<div>
    <span>And click me</span>
</div>

Upvotes: 2

Related Questions