Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine if element can have html children?

Tags:

javascript

Only Internet Explorer seems to have the element property: canHaveHtml (MSDN, Dottoro). I can't seem to find anything in other browsers to emulate it, other than using a regex with a list of tagnames. Is there a way to determine this in Chrome, Firefox etc?

For example is there a way of checking for the innerHTML property and is this 100% equivalent?

like image 731
poby Avatar asked Apr 21 '13 10:04

poby


People also ask

Can elements have child nodes?

Definition and UsageThe childNodes property returns a collection (list) of an elements's child nodes. The childNodes property returns a NodeList object. The childNodes property is read-only. childNodes[0] is the same as firstChild .

How do you find the elements of children?

To get the children of an element, you use the childNodes property of the element. How it works: First, select the element whose id is container using the querySelector() method. Then, use the childNodes property to get the children of the container elements.

Which property is used to find the child elements of an HTML element?

The children property returns a collection of an element's child elements.


1 Answers

I believe there is no specifications about that:

http://www.w3.org/TR/domcore/#concept-node-append

For instance, in Firefox, Chrome and Safari, you can actually add nodes to elements like <input> for example:

var input = document.createElement("input");
var div = document.createElement("div");

div.textContent = 'hello';

console.log(input.outerHTML, input.childNodes.length);

input.appendChild(div);

console.log(input.outerHTML, input.childNodes.length);

They're just not rendered. But they're considered children of the input node in both case. In case of Firefox the outerHTML is not changed, only the childNodes length reports 1, in case of Chrome and Safari the outerHTML changes from <input> to <input></input>. In Firefox, as opposite of Safari and Chrome, innerHTML returns actually the children's HTML, even if it's not rendered and is not returned in outerHTML.

Update: As @Bergi pointed out in @MårtenWikström answer, approaches like the previous I made doesn't really works well on element that can have content, like textarea, or even title, but not HTML content. Therefore, a better canHaveHTML could be something like that:

// Improving the `canHaveHTML` using `canHaveChildren`,
// using the approach shown by Mårten Wikström
function canHaveChildren(node) {
  // Uses the native implementation, if any.
  // I can't test on IE, so maybe it could be worthy to never use
  // the native implementation to have a consistent and controlled
  // behaviors across browsers. In case, just remove those two lines
  if (node && node.canHaveChildren)
    return node.canHaveChildren();

  // Returns false if it's not an element type node; or if it has a end tag.
  // Use the `ownerDocument` of the `node` given in order to create
  // the node in the same document NS / type, rather than the current one,
  // useful if we works across different windows / documents.
  return node.nodeType === 1 && node.ownerDocument
      .createElement(node.tagName).outerHTML.indexOf("></") > 0;
}

function canHaveHTML(node) {
  // See comment in `canHaveChildren` about native impl.
  if (node && node.canHaveHTML)
    return node.canHaveHTML();

  // We don't bother to create a new node in memory if it
  // can't have children at all
  if (!canHaveChildren(node))
    return false;

  // Can have children, then we'll check if it can have
  // HTML children.
  node = node.ownerDocument.createElement(node.tagName);

  node.innerHTML = "<b></b>";

  // if `node` can have HTML children, then the `nodeType`
  // of the node just inserted with `innerHTML` has to be `1`
  // (otherwise will be likely `3`, a textnode).
  return node.firstChild.nodeType === 1;  
}

Tested in Firefox, Chrome and Safari; that should cover all the nodes and all the scenarios.

like image 181
ZER0 Avatar answered Sep 21 '22 08:09

ZER0