HTML in XMLHttpRequest

W3C XMLHttpRequest 规范为 XMLHttpRequest添加 HTML 语法解析功能,此前仅支持 XML 语法解析。该功能允许 Web 应用程序使用XMLHttpRequest 作为解析的 DOM。

局限

为了阻止同步使用 XMLHttpRequest,HTML 在同步模式下不支持使用。并且,只有当 responseType 属性设置为 'document' 的情况下,HTML 支持才可用。这种限制避免了浪费时间解析 HTML,而传统代码在默认模式下使用 XMLHttpRequest 来检索 text/html 资源的 responseText. 此外,该限制避免了遗留代码的问题,该代码假定 HTTP 错误页面(通常具有 text/html 响应正文)的 responseXML 为空。

用法

使用 XMLHttpRequest 将 HTML 资源恢复为 DOM 就像使用 XMLHttpRequest 将 XML 资源恢复为 DOM 一样,除了您不能使用同步模式,您必须通过将字符串“document”分配给 responseType 属性来显式请求文档调用 open() 之后调用 send 之前的 XMLHttpRequest 对象。

var xhr = new XMLHttpRequest();
xhr.onload = function() {
  console.log(this.responseXML.title);
}
xhr.open("GET", "file.html");
xhr.responseType = "document";
xhr.send();

功能检测

方法 1

该方法依赖于功能的“强制异步”性质。当你尝试设置一个以“sync”方式打开的 XMLHttpRequest 对象后,尝试将设置 responseType 会在实现该功能的浏览器上引发错误,其他浏览器则运行良好。

function HTMLinXHR() {
  if (!window.XMLHttpRequest)
    return false;
  var req = new window.XMLHttpRequest();
  req.open('GET', window.location.href, false);
  try {
    req.responseType = 'document';
  } catch(e) {
    return true;
  }
  return false;
}

在 JSFiddle 中查看

This method is synchronous, does not rely on external assets though it may not be as reliable as method 2 described below since it does not check the actual feature but an indication of that feature.

方法 2

检测浏览器是否支持 XMLHttpRequest 中的 HTML 解析有两个挑战。首先,检测结果是异步获取的,因为 HTML 支持仅在异步模式下可用。Second, you have to actually fetch a test document over HTTP, because testing with a data: URL would end up testing data:URL support at the same time.

因此,为了检测 HTML 支持,服务器上需要一个测试 HTML 文件。这个测试文件很小,格式不是很完整:

<title>&amp;&<</title>

如果文件名为 detect.html,以下功能可用于检测 HTML 解析支持:

function detectHtmlInXhr(callback) {
  if (!window.XMLHttpRequest) {
    window.setTimeout(function() { callback(false); }, 0);
    return;
  }
  var done = false;
  var xhr = new window.XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState == 4 && !done) {
      done = true;
      callback(!!(this.responseXML && this.responseXML.title && this.responseXML.title == "&&<"));
    }
  }
  xhr.onabort = xhr.onerror = function() {
    if (!done) {
      done = true;
      callback(false);
    }
  }
  try {
    xhr.open("GET", "detect.html");
    xhr.responseType = "document";
    xhr.send();
  } catch (e) {
    window.setTimeout(function() {
      if (!done) {
        done = true;
        callback(false);
      }
    }, 0);
  }
}

参数 callback 是一个函数,如果 HTML 解析是支持的,则将以 true 作为唯一参数被异步调用,如果不支持 HTML 解析,则为 false。

在 JSFiddle 中查看

字符编码

如果在 HTTP Content-Type 头部中声明了字符编码,则使用该字符编码。否则,如果存在字节顺序标记,则使用由字节顺序标记指示的编码。否则,如果有一个 meta tag 声明文件的前 1024 个字节中的编码,则使用该编码。否则,文件被解码为 UTF-8。

老版本的浏览器中处理 HTML

XMLHttpRequest 最初只支持 XML 解析。HTML 解析支持是最近的一个补充。对于较老的浏览器,您甚至可以使用与正则表达式关联的 responseText 属性,以获取例如已知其 ID 的 HTML 元素的源代码:

function getHTML (oXHR, sTargetId) {
  var  rOpen = new RegExp("<(?!\!)\\s*([^\\s>]+)[^>]*\\s+id\\=[\"\']" + sTargetId + "[\"\'][^>]*>" ,"i"),
       sSrc = oXHR.responseText, aExec = rOpen.exec(sSrc);

  return aExec ? (new RegExp("(?:(?:.(?!<\\s*" + aExec[1] + "[^>]*[>]))*.?<\\s*" + aExec[1] + "[^>]*[>](?:.(?!<\\s*\/\\s*" + aExec[1] + "\\s*>))*.?<\\s*\/\\s*" + aExec[1] + "\\s*>)*(?:.(?!<\\s*\/\\s*" + aExec[1] + "\\s*>))*.?", "i")).exec(sSrc.slice(sSrc.indexOf(aExec[0]) + aExec[0].length)) || "" : "";
}

var oReq = new XMLHttpRequest();
oReq.open("GET", "yourPage.html", true);
oReq.onload = function () { console.log(getHTML(this, "intro")); };
oReq.send(null);

备注: 该解决方案对于解释器来说更为昂贵。仅在确实需要的情况下使用

Specifications

Specification
XMLHttpRequest Standard
# interface-xmlhttprequest

Browser compatibility

BCD tables only load in the browser