最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to determine if element can have html children? - Stack Overflow

programmeradmin5浏览0评论

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?

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?

Share Improve this question edited Apr 21, 2013 at 13:14 Bergi 665k161 gold badges1k silver badges1.5k bronze badges asked Apr 21, 2013 at 10:27 pobypoby 1,74618 silver badges43 bronze badges 7
  • For what purpose do you need to check the canHaveHtml property? Manybe it can be done using a different property – Shai Aharoni Commented Apr 21, 2013 at 10:34
  • Isn't it possible to add children to all tags? since its valid in html/xml? Do you have any error returned when doing so? – Grzegorz Kaczan Commented Apr 21, 2013 at 10:35
  • 1 @GrzegorzKaczan — <input> foo </input> is not valid in HTML. – Quentin Commented Apr 21, 2013 at 10:35
  • Can't test at the moment, but can you create a test using document.createElement(elementToTest).appendChild('span'); (though now I'm not sure that appendChild() can even be chained like that...) – David Thomas Commented Apr 21, 2013 at 10:39
  • 1 Just tested in Chrome, you can append a child to an <input> elements (using the HTML5 doctype), but it's not displayed anyway. So the property just doesn't have any sense in Chrome. – MaxArt Commented Apr 21, 2013 at 10:43
 |  Show 2 more ments

4 Answers 4

Reset to default 5

You can use the following function to determine whether a named element may have children.

However, as noted by ZER0, this is probably more like a replacement for IE's canHaveChildren rather than canHaveHtml, as it returns true for any tag name that is "supposed" to not be empty.

function canHaveHtml(tag) { 
    return document.createElement(tag).outerHTML.indexOf("></") > 0; 
}

It uses the fact that a newly created element, that cannot (or should not) have content, has outerHtml without an end tag.

For example:

document.createElement("input").outerHTML === "<input>"

and

document.createElement("div").outerHTML === "<div></div>"

I believe there is no specifications about that:

http://www.w3/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 ment 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.

I can't seem to find anything in other browsers to emulate it, other than using a regex with a list of tagnames.

It may not seem elegant or clever, but creating a whitelist (or blacklist) is the easiest, fastest, and most reliable approach. You don't need a regular expression; you can create use a simple structure such as an associative array.

// blacklist approach

var noChildren = {
    input: true,
    meta: true,
    br: true,
    link: true,
    img: true

    // other properties here
};

function canHaveChildren(tagName) {
    tagName = tagName.toLowerCase();
    alert(noChildren[tagName] === undefined);
}

canHaveChildren("BR");
canHaveChildren("div");

Demo: http://jsfiddle/FTbWa/
Reusable function: Github Gist

This paradigm is not without precedence; it's used in many script libraries and HTML parsers/sanitizers. For example, look at the source of jQuery and you'll notice many element-specific tests and arrays of element names and attribute names.

Here I am assuming that if the element does not have a innerHTML property, it should have a value or a src or a type property. Cant think of any other way. You could add specific TagName checks to handle specific cases which the code below fails to handle.

function CheckInnerHTMLSupport(oElement)
{
    var oDiv = document.createElement(oElement.tagName);
    if('canHaveHTML' in oDiv)
    {
        return oDiv.canHaveHTML;
    }
    var bSupportsInnerHTML = oDiv.type === undefined && oDiv.value === undefined && oDiv.src === undefined;
    return bSupportsInnerHTML;
}
发布评论

评论列表(0)

  1. 暂无评论