4

I have a bunch of custom HTML elements such ass bar-el, bar-el2, foo, foo-bar, foo-bar-2. I can do the following:

<bar-el class="custom-bar">
  <foo class="custom-foo">
    <foo-bar name="bar" class="custom-foo">
      Content
    </foo-bar>
    <foo-bar2 name="bar2" class="custom-foo">
      Content
    </foo-bar2>
  </foo>
</bar>

Unfortunately due to the code generation process it's not always possible to set class and other attributes for my custom elements, so I'd like to know if it's possible to write a CSS selector that matches all foo and foo-* elements?

1
  • Note that custom HTML tags must contain a dash (-), so <foo> is invalid while <foo-bar> is valid. Commented Aug 8, 2021 at 5:01

2 Answers 2

6

You cannot do regexp selectors on tag names. You can only perform regexps on attribute values.

You could create your own JavaScript function that fetches all elements using document.querySelectorAll('*'), do a string comparison on the tagName property for each element in the result, and only return the elements that match.

If you cannot modify the HTML to create a common attribute, I would prefer to just write out all the variants.

Sign up to request clarification or add additional context in comments.

Comments

0

While there is no CSS native way to do this, you can select your custom elements using js and apply styles to them.

const customEls = Array.from(
    document.querySelectorAll(`*`)
).filter(
    n => n.tagName?.startsWith('CUSTOM-')
);

customEls.forEach(
    el => el.classList.add('ready')
);

You can also use MutationObserver to catch any custom elements added to the DOM. Let's say you want to make all undefined custom elements from your library be display:none until after their module definitions have loaded to prevent a Flash of Unstyled Content while not unintentionally affecting any other undefined components. Just add this magic little js snippet before ANY other DOM element is loaded. Specifically, this script tag must go OUTSIDE your <html> element, directly below the <!DOCTYPE html> declaration and before ANY other element.

<!DOCTYPE html>
<script>
  const style = document.createElement('style');
  document.head.appendChild(style);

  const tagPrefix = 'custom-';
  const rule = ':not(:defined) { display: none; }';

  const observer = new MutationObserver(mutations => {
    const pre = tagPrefix.toLowerCase();
    for (const m of mutations) {
      for (const node of m.addedNodes) {
        const t = node.tagName?.toLowerCase();
        if (node.nodeType === 1 && t.startsWith(pre)) {
          style.textContent += t + rule + "\n";
        }
      }
    }
  });

  observer.observe(document, {
    childList: true, subtree: true
  });
</script>
<title>Custom Elements Partial Name Selector</title>
<custom-element-1> test </custom-element-1>
<custom-element-2> test </custom-element-2>
<script>
  customElements.define(
    'custom-element-1', class extends HTMLElement {}
  );
</script>

This works due to this note in HTML5 - 8.2.5 Tree Construction :

When the parser reaches a script element, even before the tag is encountered, it switches to the "in head" insertion mode. This allows scripts to execute during the "initial" and "before html" insertion modes, which is why scripts can run and manipulate the DOM even before explicit structural elements are parsed.

You can load this from an external file, just make sure it's cached for best performance and do not make it async or type="module" or it will not work. Additionally, you can set styles on the object from within it's own definition.

<!DOCTYPE html>
<script src="./not-defined.js"></script>
<title> External Script Example </title>
<style> .green { background: green; } </style>
<another-element> Should be visible </another-element>
<custom-element-1> Should be hidden </custom-element-1>
<custom-element-2> Should be green </custom-element-2>
<script>
  customElements.define('custom-element-2',
    class TestElement extends HTMLElement {
      constructor() { super(); this.classList.add('green'); }
  });
</script>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.