问题描述
提取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:滚动; overscroll-behavior-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;
} ...
从浏览器控制台
当您在浏览器控制台中检索 document.body.innerText
属性时,您将看到以下值:
"要查看键盘快捷键,请按问号 查看键盘快捷键 家 探索 通知 留言 轮廓 更多的 鸣叫 塞缪尔·艾略特·内桑森 @NathansonEliot 堆栈溢出 3,014 条推文 查看新推文 跟随 堆栈溢出 @堆栈溢出 通过为开发人员和所有技术人员提供服务,帮助编写未来的脚本。 纽约,NYstackoverflow.com 2010 年 4 月加入 14 关注 11.51 万粉丝 没有被您关注的任何人关注 推文 推文和回复 媒体 喜欢 Stack Overflow 的推文 你可能会喜欢 freeCodeCamp.org @freeCodeCamp 跟随 开发社区 @ThePracticalDev 跟随 展示更多 现在趋势 发生了什么 美国职业棒球大联盟 · 居住 红袜队的金莺队 红袜队趋势,加勒特理查兹 政治·趋势 他还活着 38.7K 推文 美国职业棒球大联盟·趋势 塞德里克·穆林斯 新冠肺炎 · 居住 COVID-19:马里兰州的新闻和更新 超级联赛 · 居住 曼联 VS 布莱顿霍夫阿尔比恩 趋势与#MUNBHA,#MUFC 展示更多 服务条款 隐私政策 Cookie 政策 广告信息 更多的 © 2021 Twitter,Inc."
为什么两种检索innerText的方法不同?
这还不清楚,所以如果有更多知识的人提供帮助,那就太好了。
解决方法
来自 MDN documentation of innerText
(强调我的):
注意:innerText
很容易与 Node.textContent
混淆,但两者之间存在重要区别。
基本上,innerText
知道文本呈现的外观,而 textContent
不知道。
然而,XHR 对象的响应文档永远不会被渲染,因此它没有渲染的外观。
没有 CSS 应用于文档,甚至浏览器默认设置也没有。
此时,innerText
与 textContent
的结果相同。
为了在获取资源的同时从 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 来降低风险,但您必须禁用 <iframe>
内容的 JavaScript,同时仍允许 same-origin
访问该内容。
这并不能保证内容的 JS 代码会正确运行,获取预期内容还取决于发送的 cookie 和标头是否正确。
最好考虑一种不依赖于 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) => {
...}
替换为.then((blob) => {
...}
- 用
srcdoc: text
替换src: URL.createObjectURL(blob)
- 用
- 或者在 XHR 方法中:
- 用
iframe.srcdoc = this.responseText;
替换iframe.src = URL.createObjectURL(new Blob([ this.responseText ],{ type: "text/html" }));
- 用