Frage Wie ermittelt man, ob Element HTML-Kinder haben kann?


Nur Internet Explorer scheint die Elementeigenschaft zu haben: canHaveHtml (MSDN, Dottoro). Ich kann anscheinend nichts in anderen Browsern finden, um es zu emulieren, außer eine Regex mit einer Liste von Tagnamen zu verwenden. Gibt es eine Möglichkeit, dies in Chrome, Firefox usw. zu bestimmen?

Zum Beispiel gibt es eine Möglichkeit, nach dem zu suchen innerHTML Eigentum und ist das 100% gleichwertig?


14
2018-04-21 10:27


Ursprung


Antworten:


Ich kann anscheinend nichts in anderen Browsern finden, um es nachzuahmen, andere   als eine Regex mit einer Liste von Tagnamen zu verwenden.

Es mag nicht elegant oder clever erscheinen, aber die Erstellung einer Whitelist (oder Blacklist) ist der einfachste, schnellste und zuverlässigste Ansatz. Sie brauchen keinen regulären Ausdruck. Sie können eine einfache Struktur wie ein assoziatives Array verwenden.

// 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.net/FTbWa/
Wiederverwendbare Funktion: Github Gist

Dieses Paradigma ist nicht ohne Grund; Es wird in vielen Skriptbibliotheken und HTML-Parsern / Desinfektoren verwendet. Betrachten Sie zum Beispiel die Quelle von jQuery und Sie werden viele elementspezifische Tests und Arrays von Elementnamen und Attributnamen bemerken.


3
2018-04-21 11:28



Ich glaube, dazu gibt es keine Spezifikationen:

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

Zum Beispiel in Firefox, Chrome und Safari kann fügen Sie Knoten zu Elementen hinzu wie <input> beispielsweise:

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);

Sie werden nur nicht gerendert. Aber sie gelten als Kinder der input Knoten in beiden Fällen. Im Falle von Firefox ist der outerHTML wird nicht geändert, nur die childNodes Länge Berichte 1, im Falle von Chrome und Safari die outerHTML ändert sich von <input> zu <input></input>. In Firefox, im Gegensatz zu Safari und Chrome, innerHTML gibt tatsächlich das HTML der Kinder zurück, auch wenn es nicht gerendert wird und nicht zurückgegeben wird outerHTML.

Aktualisieren: Wie @Bergi in @ MårtenWikström geantwortet hat, funktionieren Ansätze wie die vorherigen, die ich gemacht habe, auf Elementen, die Inhalte haben können, nicht wirklich gut textarea, oder auch title, aber nicht HTML-Inhalt. Daher ein besseres canHaveHTML könnte so etwas sein:

// 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;  
}

Getestet in Firefox, Chrome und Safari; das sollte alle Knoten und alle Szenarien abdecken.


4
2018-04-21 11:11



Sie können die folgende Funktion verwenden, um zu bestimmen, ob ein benanntes Element untergeordnete Elemente haben kann.

Wie jedoch von ZER0 angemerkt, ist dies wahrscheinlich eher ein Ersatz für IE canHaveChildren eher, als canHaveHtml, da es für jeden Tag-Namen gilt, der "nicht leer" sein soll.

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

Es nutzt die Tatsache, dass ein neu erstelltes Element, das keinen Inhalt haben kann (oder soll), es hat outerHtml ohne End-Tag

Beispielsweise:

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

und

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

3
2018-04-21 11:20



Hier nehme ich an, dass wenn das Element keine innerHTML-Eigenschaft hat, sollte es einen Wert oder eine src- oder eine type -Eigenschaft haben. Kann nicht anders denken. Sie könnten bestimmte TagName-Prüfungen hinzufügen, um bestimmte Fälle zu behandeln, die der unten stehende Code nicht verarbeiten kann.

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
2018-04-21 10:46