3

I want to get all of the elements unknown to the browser (and JSDOM) via document.querySelectorAll.

I've tried using a .toString then checking if the element registers as HTMLUnknownElement like so:

if (node.toString() === "[object HTMLUnknownElement]") ...

But this doesn't work if the element in question has a - in its name such as <test-element>. I assume for compatibility with HTML Custom Elements. Right now, I'm simply listing all of the elements with a load of :not's for every tag I know about, but this feels like a hack:

document.querySelectorAll("*:not(html):not(head):not(link):not(meta):not(base):not(script):not(style):not(title):not(body):not(address):not(article):not(aside):not(footer):not(h1):not(h2):not(h3):not(h4):not(h5):not(h6):not(header):not(hgroup):not(nav):not(section):not(blockquote):not(cite):not(dd):not(dl):not(div):not(dt):not(figcaption):not(figure):not(hr):not(li):not(ol):not(ul):not(menu):not(main):not(p):not(pre):not(a):not(abbr):not(b):not(bdi):not(bdo):not(br):not(code):not(data):not(time):not(dfn):not(em):not(i):not(kbd):not(mark):not(q):not(rp):not(ruby):not(rt):not(rtc):not(rb):not(s):not(del):not(ins):not(samp):not(small):not(span):not(strong):not(sub):not(sup):not(u):not(var):not(wbr):not(area):not(map):not(audio):not(source):not(img):not(track):not(video):not(embed):not(object):not(param):not(picture):not(canvas):not(noscript):not(caption):not(table):not(col):not(colgroup):not(tbody):not(tr):not(td):not(tfoot):not(th):not(thead):not(button):not(datalist):not(option):not(fieldset):not(label):not(form):not(input):not(legend):not(meter):not(optgroup):not(select):not(output):not(progress):not(textarea):not(details):not(dialog):not(menuitem):not(summary):not(content):not(slot):not(element):not(shadow):not(template):not(acronym):not(applet):not(basefont):not(font):not(big):not(blink):not(center):not(command):not(dir):not(frame):not(frameset):not(image):not(isindex):not(keygen):not(listing):not(marquee):not(multicol):not(nextid):not(noembed):not(plaintext):not(spacer):not(strike):not(tt):not(xmp)")

Does anybody know of a better way to do this?

Xenxier
  • 431
  • 4
  • 7
  • Do you consider registered custom elements as known or unknown? – str Nov 09 '17 at 13:17
  • Possible duplicate of [How to get list of registered custom elements](https://stackoverflow.com/questions/27334365/how-to-get-list-of-registered-custom-elements) – str Nov 09 '17 at 13:20
  • @str I consider them unknown until they are registered – Xenxier Nov 09 '17 at 13:27
  • There are no "standard" HTML5 tags with hyphens in them, so why not just check if the tagName contains a hyphen character? Assume those are custom elements combined with your current method. – skyline3000 Nov 09 '17 at 13:29
  • 1
    @skyline3000 Almost correct. There are actually some [elements containing hyphens](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name) but they can probably be ignored in this case. – str Nov 09 '17 at 13:32
  • @skyline3000 I also want to check for elements the browser doesn't recognise which aren't traditional custom elements (e.g. ``). I suppose I could bundle a hyphen check with the `toString` method, but this still seems like a hack. – Xenxier Nov 09 '17 at 13:33
  • 1
    @Xenxier [This document](https://html.spec.whatwg.org/multipage/dom.html#htmlunknownelement) could give you some background about `HTMLElement` and `HTMLUnknownElement`. – str Nov 09 '17 at 13:35

2 Answers2

1

The other answer did not find custom elements correctly for my application.

I changed the snippet and the following works for me

function isUnknownElement(element) {
  const tagName = element.tagName

  if (tagName.includes('-')) {
    return element.constructor.name === 'HTMLUnknownElement' ||
      element.constructor.name === 'HTMLElement'
  }

  return element.constructor.name === 'HTMLUnknownElement';
}

function identifyUnknownElements(selector) {
  const elements = document.querySelectorAll(selector);
  const unknownElements = [];

  for (let i = 0, length = elements.length; i < length; i++) {
    if (isUnknownElement(elements[i])) {
      elements[i].classList.add('unknown-element');
      unknownElements.push(elements[i]);
    }

  }

  return unknownElements;
}

console.log(identifyUnknownElements('*'))
Raynos
  • 166,823
  • 56
  • 351
  • 396
  • This seems like a solid solution, but rather than using constructor.name could we use the `instanceof` keyword instead? – Xenxier May 10 '22 at 17:30
0

Hope this will help,

function isUnknownElement(element) {
  return element.constructor.name === 'HTMLUnknownElement';
}

function identifyUnknownElements(selector) {
  const elements = document.querySelectorAll(selector);
  const unknownElements = [];

  for (let i = 0, length = elements.length; i < length; i++) {

    if (/-/g.test(elements[i].nodeName)) {
      const temp = document.createElement(elements[i].nodeName.replace(/-+/, ""));
      if (isUnknownElement(temp)) {
        elements[i].classList.add('unknown-element');
        unknownElements.push(elements[i]);
      }
      continue;
    }
    if (isUnknownElement(elements[i])) {
      elements[i].classList.add('unknown-element');
      unknownElements.push(elements[i]);
    }

  }

  return unknownElements;
}

console.log(identifyUnknownElements('*'))
<temp></temp>
<test-element></test-element>
0xdw
  • 3,755
  • 2
  • 25
  • 40