【问题标题】:Why does the document.body.innerText attribute of XMLHttpRequest differ from the document.body.innerText attribute in the browser?为什么 XMLHttpRequest 的 document.body.innerText 属性与浏览器中的 document.body.innerText 属性不同?
【发布时间】:2021-06-30 20:25:14
【问题描述】:

提取document.body.innerText

我目前正在开展一个项目,旨在改善残疾儿童的网络可访问性。但是,我最近在尝试从 XMLHttpRequest 响应中检索 document.body.innerText 属性时遇到了一个巨大的障碍。

XMLHttpRequest (responseXML.body.innerText) 的结果与直接从浏览器控制台中调用 document.body.innerText 的结果不同。

使用 XMLHttpRequest

这是相关代码示例,目前在我的 Chrome 浏览器扩展程序(Chrome Canary v91.0.4466.0, x86_64)上运行

    if (!window.XMLHttpRequest) return;

    // Create new request
    var xhr = new XMLHttpRequest();

    // Setup callback
    xhr.onload = function () {
        console.log(this.responseXML.body.innerText));
    }

    // Get the HTML
    xhr.open('GET', searchResultURL);
    xhr.responseType = 'document';
    xhr.send();

在 StackOverFlow 的 Twitter page 上运行上述代码时,您会看到 this.responseXML.body.innerText 的值包含 CSS。

身体{ -ms-overflow-style:滚动条; 溢出-y:滚动; 过度滚动行为-y:无; }

.errorContainer {
  background-color: #FFF;
  color: #0F1419;
  max-width: 600px;
  margin: 0 auto;
  padding: 10%;
  font-family: Helvetica, sans-serif;
  font-size: 16px;
}

.errorButton {
  margin: 3em 0;
}

.errorButton a {
  background: #1DA1F2;
  border-radius: 2.5em;
  color: white;
  padding: 1em 2em;
  text-decoration: none;
} ...

Full response string

从浏览器控制台

当您在浏览器控制台中检索document.body.innerText 属性时,您将看到以下值:

"要查看键盘快捷键,请按问号 查看键盘快捷键 家 探索 通知 留言 轮廓 更多的 鸣叫 塞缪尔·艾略特·内森森 @内森森艾略特 堆栈溢出 3,014 条推文 查看新推文 跟随 堆栈溢出 @堆栈溢出 通过为开发人员和所有技术人员服务来帮助编写未来的脚本。 纽约,NYstackoverflow.com2010 年 4 月加入 14 关注 115.1K 追随者 没有被您关注的任何人关注 推文 推文和回复 媒体 喜欢 Stack Overflow 的推文 你可能会喜欢 freeCodeCamp.org @freeCodeCamp 跟随 开发者社区 @ThePracticalDev 跟随 展示更多 现在趋势 发生了什么 美国职业棒球大联盟 · 居住 红袜队的金莺队 红袜队的趋势,加勒特理查兹 政治 · 趋势 他还活着 38.7K 条推文 美国职业棒球大联盟 · 趋势 塞德里克·穆林斯 新冠肺炎 · 居住 COVID-19:马里兰州的新闻和更新 超级联赛 · 居住 曼联 VS 布莱顿 与#MUNBHA、#MUFC 一起流行 展示更多 服务条款 隐私政策 Cookie 政策 广告信息 更多的 © 2021 Twitter, Inc."

为什么这两种检索innerText的方法不同?

这还不清楚,所以如果有更多知识的人提供帮助会很棒。

【问题讨论】:

    标签: javascript html dom xmlhttprequest innertext


    【解决方案1】:

    来自MDN documentation of innerText(强调我的):

    注意:innerText 很容易与Node.textContent 混淆,但两者之间存在重要区别。 基本上,innerText 知道文本的呈现外观,而textContent 则不知道。

    但是,XHR 对象的响应文档从不渲染,因此它没有渲染的外观。 没有 CSS 应用于文档,甚至浏览器默认值也没有。 此时innerTexttextContent的结果相同。

    为了在获取资源的同时从innerText 中受益,您可以强制浏览器呈现文档,例如在<iframe> 中。 直接设置<iframe> 就足够了,即无需任何 XHR 或 Fetch 请求。

    const iframe = Object.assign(document.createElement("iframe"), {
        src: searchResultURL
      });
    
    // This CSS ensures that the <iframe> is hidden away, but still rendered.
    Object.assign(iframe.style, {
      position: "absolute",
      top: "-100%"
    });
    document.body.appendChild(iframe);
    
    // This load listener ensures that the fetched document is rendered (i.e. CSS applied).
    iframe.addEventListener("load", ({target: {contentDocument: {body: {innerText}}}}) => {
      console.log(innerText); // Use the `innerText` here!
      iframe.remove();
    });
    

    请注意,这种方法和类似方法仅在 CORS 标头允许访问框架资源时才有效。 否则不容易。

    但是,这样做存在安全风险。 您可以尝试使用sandbox attribute 来降低风险,但您必须禁用&lt;iframe&gt; 内容的JavaScript,同时仍允许same-origin 访问内容。 这并不能保证内容的 JS 代码会正确运行,获得预期的内容还取决于发送的正确 cookie 和 headers。

    考虑一种不依赖于innerText 行为的方法可能会更好。


    即使(可能)没有必要,也可以将 XHR 或更现代的 Fetch 混入其中,这样您就可以看到一些替代方法。

    以下代码使用更现代的Fetch API,而不是您原来的 XHR 方法。

    fetch(searchResultURL)
      .then((resp) => resp.text())
      .then((text) => {
        const iframe = Object.assign(document.createElement("iframe"), {
            srcdoc: text
          });
        
        Object.assign(iframe.style, {
          position: "absolute",
          top: "-100%"
        });
        document.body.appendChild(iframe);
        iframe.addEventListener("load", ({target: {contentDocument: {body: {innerText}}}}) => {
          console.log(innerText);
          iframe.remove();
        });
      });
    

    或者,使用您原来的 XHR 方法和更向后兼容的旧浏览器代码:

    • 删除xhr.responseType = 'document';

    • 将此用作load 侦听器(改用addEventListener):

      xhr.addEventListener("load", function(){
        var iframe = document.createElement("iframe");
      
        iframe.srcdoc = this.responseText;
        iframe.style.position = "absolute";
        iframe.style.top = "-100%";
        iframe.addEventListener("load", function(){
          console.log(this.contentDocument.body.innerText);
          document.body.removeChild(iframe);
        });
        document.body.appendChild(iframe);
      });
      

    这仍然需要srcdoc 在您的浏览器中工作。 或者,Blob API 具有更好的浏览器支持:

    • 在 Fetch 方法中:
      • resp.text() 替换为resp.blob()
      • .then((text) =&gt; {} 替换为.then((blob) =&gt; {}
      • srcdoc: text 替换为src: URL.createObjectURL(blob)
    • 或者在 XHR 方法中:
      • iframe.srcdoc = this.responseText; 替换为iframe.src = URL.createObjectURL(new Blob([ this.responseText ], { type: "text/html" }));

    【讨论】:

    • 在加载 iframe 时,您不妨放弃 XHR/fetch 调用并直接将 iframe 的 src 设置为 URL :-) 另请注意,这是一个潜在的安全问题,因为在 iframe 中呈现文档将执行其中包含的脚本。
    • @Bergi 是的,我在我的编辑中包含了没有 Fetch 的更简单的方法,但我可以在另一个编辑中澄清这一点。您是否知道是否可以访问&lt;iframe&gt; 中的页面 iff Fetch 或 XHR 可以获取该页面;或者这两种方法可能存在一些差异?是的,存在安全问题;可以使用sandbox 属性来缓解它,但我认为答案的最后一段仍然有效:这仍然不能保证成功。
    • 啊,太好了,我写评论的时候还没有看到编辑。
    猜你喜欢
    • 2015-03-22
    • 2020-11-15
    • 2018-02-16
    • 1970-01-01
    • 2013-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    相关资源
    最近更新 更多