Как определить, может ли элемент иметь html-детей? - программирование
Подтвердить что ты не робот

Как определить, может ли элемент иметь html-детей?

Только у Internet Explorer есть свойство элемента: canHaveHtml (MSDN, Dottoro). Кажется, я не могу найти что-либо в других браузерах для эмуляции, кроме использования регулярного выражения со списком тэгов. Есть ли способ определить это в Chrome, Firefox и т.д.

Например, есть способ проверить свойство innerHTML и соответствует ли это 100%?

4b9b3361

Ответ 1

Я не могу найти что-либо в других браузерах, чтобы подражать этому, другие чем использование регулярного выражения со списком тэгов.

Это может показаться не изящным или умным, но создание белого списка (или черного списка) - это самый простой, быстрый и надежный подход. Вам не нужно регулярное выражение; вы можете создать простую структуру, такую ​​как ассоциативный массив.

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

Демо: http://jsfiddle.net/FTbWa/
Многоразовая функция: Github Gist

Эта парадигма не имеет приоритета; он использовался во многих библиотеках script и в HTML-парсерах/дезинфицирующих средствах. Например, посмотрите на источник jQuery, и вы заметите множество специфичных для элемента тестов и массивов имен элементов и имен атрибутов.

Ответ 2

Я считаю, что нет никаких спецификаций:

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

Например, в Firefox, Chrome и Safari вы можете фактически добавлять узлы к таким элементам, как <input>, например:

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

Они просто не отображаются. Но в обоих случаях они считаются детьми input node. В случае Firefox outerHTML не изменяется, только отчеты childNodes 1, в случае Chrome и Safari значение outerHTML изменяется от <input> до <input></input>. В Firefox, напротив Safari и Chrome, innerHTML возвращает на самом деле дочерний HTML, даже если он не отображается и не возвращается в outerHTML.

Обновление: Как заметил @Bergi в ответе @MårtenWikström, подходы, подобные предыдущему, которые я сделал, на самом деле не очень хорошо работают на элементе, который может содержать контент, например textarea или даже title, но не содержимое HTML. Поэтому лучше canHaveHTML может быть что-то вроде этого:

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

Протестировано в Firefox, Chrome и Safari; который должен охватывать все узлы и все сценарии.

Ответ 3

Вы можете использовать следующую функцию, чтобы определить, может ли именованный элемент иметь дочерние элементы.

Однако, как отмечено ZER0, это скорее похоже на замену IE canHaveChildren, а не canHaveHtml, поскольку он возвращает true для любого имени тега, которое "предполагается" не должно быть пустым.

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

Он использует тот факт, что вновь созданный элемент, который не может (или не должен) иметь контент, имеет outerHtml без конечного тега.

Например:

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

и

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

Ответ 4

Здесь я предполагаю, что если элемент не имеет свойства innerHTML, он должен иметь значение или свойство src или type. Не могу думать иначе. Вы можете добавить определенные проверки тегов TagName для обработки конкретных случаев, с которыми нижеприведенный код не справляется.

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