Подтвердить что ты не робот

Как создать объекты Document с JavaScript

В основном, что вопрос, как предполагается построить объект Document из строки HTML динамически в javascript?

4b9b3361

Ответ 1

В спецификациях определены два метода: createDocument из DOM Core Level 2 и createHTMLDocument из HTML5. Первый создает XML-документ (включая XHTML), последний создает HTML-документ. Оба они находятся как функции в интерфейсе DOMImplementation.

var impl    = document.implementation,
    xmlDoc  = impl.createDocument(namespaceURI, qualifiedNameStr, documentType),
    htmlDoc = impl.createHTMLDocument(title);

В действительности эти методы довольно молоды и реализованы только в последних версиях браузеров. Согласно http://quirksmode.org и MDN, следующие браузеры поддерживают createHTMLDocument:

  • Chrome 4
  • Opera 10
  • Firefox 4
  • Internet Explorer 9
  • Safari 4

Интересно, что вы можете (вроде) создать HTML-документ в более старых версиях Internet Explorer, используя ActiveXObject:

var htmlDoc = new ActiveXObject("htmlfile");

Результирующим объектом будет новый документ, который можно манипулировать так же, как и любой другой документ.

Ответ 2

Предполагая, что вы пытаетесь создать полностью проанализированный объект Document из строки разметки и типа контента, который вам также известен (возможно, потому, что вы получили html из xmlhttprequest и, таким образом, получили тип содержимого в своем Content-Type http header, вероятно, обычно text/html) - это должно быть легко:

var doc = (new DOMParser).parseFromString(markup, mime_type);

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

У вас, вероятно, есть более простая (но все же беспорядочная) проблема наличия строки html, для которой вы хотите получить полностью проанализированный объект Document. Вот еще один вопрос о том, как это сделать, что также должно работать во всех браузерах - сначала вы создаете объект HTML Document:

var doc = document.implementation.createHTMLDocument('');

а затем заполнить его вашим html-фрагментом:

doc.open();
doc.write(html);
doc.close();

Теперь у вас должен быть полностью проанализированный DOM в doc, который вы можете запустить alert(doc.title) on, срез с селекторами css, например doc.querySelectorAll('p') или ditto XPath, используя doc.evaluate.

Это действительно работает в современных браузерах WebKit, таких как Chrome и Safari (я только что протестировал в Chrome 22 и Safari 6 соответственно) - вот пример, который использует текущий исходный код страницы, воссоздает его в новой переменной документа src, читает его название, перезаписывает его с помощью цитированной версии html того же исходного кода и показывает результат в iframe: http://codepen.io/johan/full/KLIeE

К сожалению, я не думаю, что у каких-либо других современных браузеров все еще есть такие же надежные реализации.

Ответ 3

В спецификации (doc) можно использовать метод createHTMLDocument DOMImplementation, доступный через document.implementation как следующим образом:

var doc = document.implementation.createHTMLDocument('My title');  
var body = document.createElementNS('http://www.w3.org/1999/xhtml', 'body'); 
doc.documentElement.appendChild(body);
// and so on

Ответ 4

Обновленный ответ на 2014 год, когда DOMparser развился. Это работает во всех существующих браузерах, которые я могу найти, и должен работать в более ранних версиях IE, используя вышеприведенный подход ecManaut document.implementation.createHTMLDocument('').

По сути, IE, Opera, Firefox могут разбираться как "text/html". Safari анализирует как "text/xml".

Остерегайтесь нетерпимого анализа XML. Параметр Safari будет разбит на неразрывные пробелы и другие символы HTML (французские/немецкие акценты), обозначенные амперсандами. Вместо того, чтобы обрабатывать каждый символ отдельно, код ниже заменяет все амперсанды бессмысленной символьной строкой "j! J!". Эта строка впоследствии может быть повторно отображена как амперсанд при отображении результатов в браузере (проще, я нашел, чем пытаться обрабатывать амперсанды в "ложном" разборе XML).

function parseHTML(sText) {
try {

    console.log("Domparser: " + typeof window.DOMParser);

    if (typeof window.DOMParser !=null) {
        // modern IE, Firefox, Opera  parse text/html
        var parser = new DOMParser();
        var doc = parser.parseFromString(sText, "text/html");
        if (doc != null) {
            console.log("parsed as HTML");
            return doc

        }
        else {

            //replace ampersands with harmless character string to avoid XML parsing issues
            sText = sText.replace(/&/gi, "j!J!");
            //safari parses as text/xml
            var doc = parser.parseFromString(sText, "text/xml");
            console.log("parsed as XML");
            return doc;
        }

    } 
    else  {
        // older IE 
        doc= document.implementation.createHTMLDocument('');
        doc.write(sText);           
        doc.close;
        return doc; 
    }
} catch (err) {
    alert("Error parsing html:\n" + err.message);
}
}

Ответ 5

В большинстве распространенных браузеров работает, но не некоторые. Так просто быть (но не так):

// Fails if UA doesn't support parseFromString for text/html (e.g. IE)
function htmlToDoc(markup) {
  var parser = new DOMParser();
  return parser.parseFromString(markup, "text/html");
}

var htmlString = "<title>foo bar</title><div>a div</div>";
alert(htmlToDoc(htmlString).title);

Чтобы учесть кавычки пользовательского агента, может быть лучше (обратите внимание на атрибуцию):

/*
 * DOMParser HTML extension
 * 2012-02-02
 *
 * By Eli Grey, http://eligrey.com
 * Public domain.
 * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
 *
 * Modified to work with IE 9 by RobG
 * 2012-08-29
 *
 * Notes:
 *
 *  1. Supplied markup should be avalid HTML document with or without HTML tags and
 *     no DOCTYPE (DOCTYPE support can be added, I just didn't do it)
 *
 *  2. Host method used where host supports text/html
 */

/*! @source https://gist.github.com/1129031 */
/*! @source https://developer.mozilla.org/en-US/docs/DOM/DOMParser */

/*global document, DOMParser*/

(function(DOMParser) {
    "use strict";

    var DOMParser_proto;
    var real_parseFromString;
    var textHTML;         // Flag for text/html support
    var textXML;          // Flag for text/xml support
    var htmlElInnerHTML;  // Flag for support for setting html element innerHTML

    // Stop here if DOMParser not defined
    if (!DOMParser) return;

    // Firefox, Opera and IE throw errors on unsupported types
    try {
        // WebKit returns null on unsupported types
        textHTML = !!(new DOMParser).parseFromString('', 'text/html');

    } catch (er) {
      textHTML = false;
    }

    // If text/html supported, don't need to do anything.
    if (textHTML) return;

    // Next try setting innerHTML of a created document
    // IE 9 and lower will throw an error (can't set innerHTML of its HTML element)
    try {
      var doc = document.implementation.createHTMLDocument('');
      doc.documentElement.innerHTML = '<title></title><div></div>';
      htmlElInnerHTML = true;

    } catch (er) {
      htmlElInnerHTML = false;
    }

    // If if that failed, try text/xml
    if (!htmlElInnerHTML) {

        try {
            textXML = !!(new DOMParser).parseFromString('', 'text/xml');

        } catch (er) {
            textHTML = false;
        }
    }

    // Mess with DOMParser.prototype (less than optimal...) if one of the above worked
    // Assume can write to the prototype, if not, make this a stand alone function
    if (DOMParser.prototype && (htmlElInnerHTML || textXML)) { 
        DOMParser_proto = DOMParser.prototype;
        real_parseFromString = DOMParser_proto.parseFromString;

        DOMParser_proto.parseFromString = function (markup, type) {

            // Only do this if type is text/html
            if (/^\s*text\/html\s*(?:;|$)/i.test(type)) {
                var doc, doc_el, first_el;

                // Use innerHTML if supported
                if (htmlElInnerHTML) {
                    doc = document.implementation.createHTMLDocument("");
                    doc_el = doc.documentElement;
                    doc_el.innerHTML = markup;
                    first_el = doc_el.firstElementChild;

                // Otherwise use XML method
                } else if (textXML) {

                    // Make sure markup is wrapped in HTML tags
                    // Should probably allow for a DOCTYPE
                    if (!(/^<html.*html>$/i.test(markup))) {
                        markup = '<html>' + markup + '<\/html>'; 
                    }
                    doc = (new DOMParser).parseFromString(markup, 'text/xml');
                    doc_el = doc.documentElement;
                    first_el = doc_el.firstElementChild;
                }

                // RG: I don't understand the point of this, I'll leave it here though 
                //     In IE, doc_el is the HTML element and first_el is the HEAD.
                //
                // Is this an entire document or a fragment?
                if (doc_el.childElementCount == 1 && first_el.localName.toLowerCase() == 'html') {
                    doc.replaceChild(first_el, doc_el);
                }

                return doc;

            // If not text/html, send as-is to host method
            } else {
                return real_parseFromString.apply(this, arguments);
            }
        };
    }
}(DOMParser));

// Now some test code
var htmlString = '<html><head><title>foo bar</title></head><body><div>a div</div></body></html>';
var dp = new DOMParser();
var doc = dp.parseFromString(htmlString, 'text/html');

// Treat as an XML document and only use DOM Core methods
alert(doc.documentElement.getElementsByTagName('title')[0].childNodes[0].data);

Не откладывайте на количество кода, есть много комментариев, его можно немного сократить, но он становится менее читаемым.

О, и если разметка действительна XML, жизнь намного проще:

var stringToXMLDoc = (function(global) {

  // W3C DOMParser support
  if (global.DOMParser) {
    return function (text) {
      var parser = new global.DOMParser();
      return parser.parseFromString(text,"application/xml");
    }

  // MS ActiveXObject support
  } else {
    return function (text) {
      var xmlDoc;

      // Can't assume support and can't test, so try..catch
      try {
        xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
        xmlDoc.async="false";
        xmlDoc.loadXML(text);
      } catch (e){}
      return xmlDoc;
    }
  }
}(this));


var doc = stringToXMLDoc('<books><book title="foo"/><book title="bar"/><book title="baz"/></books>');
alert(
  doc.getElementsByTagName('book')[2].getAttribute('title')
);