防止删除 Tumblr 仪表板上的 DOM 个元素
Prevent removal of DOM elements on the Tumblr dashboard
我决定通过 #posts
元素内的扩展程序将 post 显示在仪表板上。现在我的问题是 Tumblr 在滚动时自动删除 post 的内容。
这里是一个正常的例子post 删除前:
<li class="post_container" data-pageable="post_110141415125">
<div class="post post_full ..." [...]>
<!-- ... -->
</div>
</li>
同样post,删除其内容后:
<li class="post_container" data-pageable="post_110141415125" style="border-radius: 3px; height: 207px; width: 540px; background-color: rgb(255, 255, 255);">
</li>
因为我的扩展程序将 posts 重新定位到 pinterest-like 布局中,删除了 posts ' 内容打乱了整个布局。是否可以阻止 Tumblr 仪表板删除每个 .post
中的 DOM 元素?
编辑:
我在我的内容脚本中为 removeChild
方法添加了这个注入代码:
function addScript(){
var script = document.createElement('script');
script.id = 'newRemoveChildScript'; ///just to find is faster in the code
script.src = chrome.extension.getURL("injectedCode.js");
$($('script')[0]).before(script); ///Script is indeed present, I checked that
}
window.onload = addScript;
我接下来要做的是用第二个版本创建 injectedCode.js
文件,如下所示:
(function (obj, method) {
function isPost(el) {
return (/^post_\d+$/.test(el.id) && ~el.className.indexOf('post'));
}
obj[method] = (function (obj_method) {
return function (child) {
if (isPost(child)) return child;
return obj_method.apply(this, arguments);
}
}(obj[method]));
}(Element.prototype, 'removeChild'));
当我加载 Tumblr-Dashboard 时,我可以确认 Script-element 存在于 head-element 中。该文件也已正确链接,因为 'removeChild' 函数被调用了一次。似乎那里正在删除一个脚本元素。
从那时起,该函数再也不会被调用,我可以再次看到那些 'empty' posts。
劫持 Tumblr 仪表板行为
免责声明:这里发生的事情完全合法,不要惊慌!
你要问的是做起来并不容易,但实际上你可以阻止 Tumblr 在滚动时隐藏 posts在视口之外。更准确地说,高级程序员实际上可以操纵任何 Tumblr 方法,使其按照他们想要的方式运行,但是,现在,让我们专注于您的问题。
为什么 Tumblr 在滚动时删除 posts?
Tumblr 删除了 post 的内容以使页面更轻 并让您继续浏览而不会遇到任何滞后 and/or 由于巨大的滚动困难元素的数量。想象一下连续向下滚动一个小时:你的破折号将容纳数千个 posts,并且你的浏览器可能会因为每次滚动时必须移动的东西过多而冻结。
Tumblr 如何做到这一点?
我没有检查和理解 Tumblr 代码的每一行,因为它都是 packed/minified,并且不可能真正理解一行任何方法(除非你在 Tumblr 工作,在这种情况下,恭喜),但是,基本上,我放了一些 DOM breakpoints 并且我检查了当 post 离开视口时删除的代码部分。
简而言之,这就是它的作用:
Tumblr 为 post 创造了一些自定义和狡猾的“滚动”事件。每当 post 超出视口 .
一定距离时,这些事件就会触发
现在 Tumblr 将一些侦听器附加到这些自定义事件,并且,在 li.post_container
元素 上使用 DOM 断点,您'将能够看到这些侦听器驻留在 dashboard.js
JavaScript 文件中。
更准确地说,删除 post 内容的确切代码部分如下所示:
function j(t,v) { try{ t.removeChild(v) } catch(u){} }
其中 t
是 li.post_container
,v
是其子项:div#post_[id]
.
这部分代码是做什么的?没什么特别的:它从字面上 从其父元素 li.post_container
.
中删除了 div#post_[id]
元素
现在 Tumblr 必须 样式删除 post 的容器 以保持 #posts
元素的高度,其中包含所有 post,否则删除 post 的内容会使页面向下滚动 H
像素,其中 H 是 post 的高度被移除了。
此更改发生在另一个脚本中,即 index.js
,我什至没有仔细查看它,因为它根本无法阅读:
顺便说一句,这个脚本并不重要,因为我们可以使用简单的CSSs来防止样式更改.
现在 post 的内容已被“删除”,它看起来像这样:
*为了清楚起见:我添加了文本和墓碑,你实际上看不到它们 lol
所以,最后,当 post 离开视口时,变化如下:
<li class="post_container" [...]>
<div id="post_[id]" class="post post_full ..." [...]>
<!-- post body... -->
</div>
</li>
对此:
<li class="post_container" [...] style="border-radius: 3px; height: Hpx; width: Wpx; background-color: rgb(255, 255, 255);">
</li>
防止 Tumblr 代码删除 posts(确实是一些小技巧)
现在我们知道 Tumblr 在仪表盘中滚动时的行为方式,我们可以防止它删除 post 并将它们设置样式 看起来像空白的死块带有圆形边框。
风格第一
在关注防止 post 被删除之前,我们应该将它们设置为不具有固定的高度和宽度,覆盖 Tumblr 在它们离开视口时应用到它们的样式。
我们可以在页面中注入一些简单的 CSS,制定规则 !important
以防止内联样式覆盖它们。这是我们要添加的 CSS:
li.post_container {
height: auto !important;
width: 540px !important;
/* 540px is the default width
* change it to any amount that applies to your layout
*/
}
或者,使用 JavaScript,我们可以:
var s = document.createElement('style');
s.textContent = 'li.post_container{height:auto!important;width:540px!important;}';
document.head.appendChild(s);
防止 post 删除:简单(愚蠢)的方法
如果我们不想浪费时间思考一个好的解决方案,我们可以只实现最简单的一个,并且,在几行代码中我们可以:
复制 Element.prototype
的 removeChild
方法,并将其命名为 _removeChild
.
将原来的替换为“过滤”对它的调用的函数,决定删除除posts之外的所有内容。
这是解决问题的简单但愚蠢的代码:
Element.prototype._removeChild = Element.prototype.removeChild
Element.prototype.removeChild = function(child) {
// Check if the element is a post (e.g. the id looks like "post_123456...")
if (/^post_\d+$/.test(child.id)) {
// If so, stop here by returning the post
// This will make Tumblr believe the post has been removed
return child;
}
// Otherwise the element isn't a post, so we can remove it
return Element.prototype._removeChild.apply(this, arguments);
}
无注释代码长度:5行
您显然可以检查多个条件:例如,您可以检查 post 是否包含 classes post
和 post_full
,或者它是否parentElement
有classpost_container
,等等...由你决定。
防止 post 删除:聪明(更难)的方法
完成此操作的一种更优雅但更巧妙的方法是“编辑”原始Element.prototype.removeChild
方法,使用一些闭包和更复杂的逻辑。这是我们要做的:
创建一个闭包,我们将在其中 重新定义 Element.prototype.removeChild
方法 为 post 添加一个“过滤器” s.
在这个闭包中,我们将定义我们的自定义过滤器函数,我们可以调用它 isPost()
,以 过滤将要删除的元素.
使用第二个闭包,我们将重新定义 Element.prototype.removeChild
方法,使其行为如下:
检查要删除的元素 使用我们之前创建的过滤器函数 isPost()
.
如果要移除的元素是post,则停止这里,不要移除。
否则继续并使用.apply
方法调用原来的Element.prototype.removeChild
方法
看起来可能很难,但是代码并不难理解,我用注释完成了它,使它更容易理解,这里是:
(function (obj, method) {
// This is all wrapped inside a closure
// to make the new removeChild method remember the isPost function
// and maintain all the work invisible to the rest of the code in the page
function isPost(el) {
// Check if the element is a post
// This will return true if el is a post, otherwise false
return (/^post_\d+$/.test(el.id) && ~el.className.indexOf('post'));
}
// Edit the Element.prototype.removeChild assigning to it our own function
obj[method] = (function (obj_method) {
return function (child) {
// This is the function that will be assigned to removeChild
// It is wrapped in a closure to make the check before removing the element
// Check if the element that is going to be removed is a post
if (isPost(child)) return child;
// If so, stop here by returning the post
// This will make Tumblr believe the post has been removed
// Otherwise continue and remove it
return obj_method.apply(this, arguments);
}
}(obj[method]));
}(Element.prototype, 'removeChild'));
无注释代码长度:11行
在页面中注入脚本
由于 内容脚本在隐藏的、不同的 window
对象中工作,因此 内容脚本代码无法与页面代码交互,您必须从您的内容脚本直接在页面中注入代码。为此,您需要创建一个 <script>
元素并将其插入到页面的 <head>
中。您可以将要注入的代码存储在一个新脚本中(我们称之为 injectedCode.js
)并使用 chrome.runtime.getURL
获取绝对 URL 以在您的 <script>
src 中使用。
重要提示:您需要将 injectedCode.js
文件添加到您的 manifest.json
中的 "web_accessible_resources"
field 以使其可以从 Tumblr 访问,像这样:
"web_accessible_resources": ["/injectedCode.js"]
然后您可以创建一个 <script>
,将 .src
设置为您的 injectedCode.js
,如下所示:
/* In your content script: */
var s = document.createElement('script');
s.src = chrome.extension.getURL("/injectedCode.js");
document.head.appendChild(s);
尾注
这些只是完成您想要做的事情的两种方法:防止 Tumblr 在滚动时隐藏 posts。显然还有许多其他方法可以做到这一点,例如使用 MutationObserver
s 检查子树修改,删除所有 posts 并创建一个包含新个人事件和方法的新容器来模拟 Tumblr 的,插入Tumblr代码并直接编辑(几乎不可能,合法性也不高),等等。
我还想强调一个事实,这只是对一个问题的回答,我不负责 错误地使用此类操作来伤害您的扩展用户。
我决定通过 #posts
元素内的扩展程序将 post 显示在仪表板上。现在我的问题是 Tumblr 在滚动时自动删除 post 的内容。
这里是一个正常的例子post 删除前:
<li class="post_container" data-pageable="post_110141415125">
<div class="post post_full ..." [...]>
<!-- ... -->
</div>
</li>
同样post,删除其内容后:
<li class="post_container" data-pageable="post_110141415125" style="border-radius: 3px; height: 207px; width: 540px; background-color: rgb(255, 255, 255);">
</li>
因为我的扩展程序将 posts 重新定位到 pinterest-like 布局中,删除了 posts ' 内容打乱了整个布局。是否可以阻止 Tumblr 仪表板删除每个 .post
中的 DOM 元素?
编辑:
我在我的内容脚本中为 removeChild
方法添加了这个注入代码:
function addScript(){
var script = document.createElement('script');
script.id = 'newRemoveChildScript'; ///just to find is faster in the code
script.src = chrome.extension.getURL("injectedCode.js");
$($('script')[0]).before(script); ///Script is indeed present, I checked that
}
window.onload = addScript;
我接下来要做的是用第二个版本创建 injectedCode.js
文件,如下所示:
(function (obj, method) {
function isPost(el) {
return (/^post_\d+$/.test(el.id) && ~el.className.indexOf('post'));
}
obj[method] = (function (obj_method) {
return function (child) {
if (isPost(child)) return child;
return obj_method.apply(this, arguments);
}
}(obj[method]));
}(Element.prototype, 'removeChild'));
当我加载 Tumblr-Dashboard 时,我可以确认 Script-element 存在于 head-element 中。该文件也已正确链接,因为 'removeChild' 函数被调用了一次。似乎那里正在删除一个脚本元素。
从那时起,该函数再也不会被调用,我可以再次看到那些 'empty' posts。
劫持 Tumblr 仪表板行为
免责声明:这里发生的事情完全合法,不要惊慌!
你要问的是做起来并不容易,但实际上你可以阻止 Tumblr 在滚动时隐藏 posts在视口之外。更准确地说,高级程序员实际上可以操纵任何 Tumblr 方法,使其按照他们想要的方式运行,但是,现在,让我们专注于您的问题。
为什么 Tumblr 在滚动时删除 posts?
Tumblr 删除了 post 的内容以使页面更轻 并让您继续浏览而不会遇到任何滞后 and/or 由于巨大的滚动困难元素的数量。想象一下连续向下滚动一个小时:你的破折号将容纳数千个 posts,并且你的浏览器可能会因为每次滚动时必须移动的东西过多而冻结。
Tumblr 如何做到这一点?
我没有检查和理解 Tumblr 代码的每一行,因为它都是 packed/minified,并且不可能真正理解一行任何方法(除非你在 Tumblr 工作,在这种情况下,恭喜),但是,基本上,我放了一些 DOM breakpoints 并且我检查了当 post 离开视口时删除的代码部分。
简而言之,这就是它的作用:
Tumblr 为 post 创造了一些自定义和狡猾的“滚动”事件。每当 post 超出视口 .
一定距离时,这些事件就会触发现在 Tumblr 将一些侦听器附加到这些自定义事件,并且,在
li.post_container
元素 上使用 DOM 断点,您'将能够看到这些侦听器驻留在dashboard.js
JavaScript 文件中。更准确地说,删除 post 内容的确切代码部分如下所示:
function j(t,v) { try{ t.removeChild(v) } catch(u){} }
其中
t
是li.post_container
,v
是其子项:div#post_[id]
.这部分代码是做什么的?没什么特别的:它从字面上 从其父元素
中删除了li.post_container
.div#post_[id]
元素现在 Tumblr 必须 样式删除 post 的容器 以保持
#posts
元素的高度,其中包含所有 post,否则删除 post 的内容会使页面向下滚动H
像素,其中 H 是 post 的高度被移除了。此更改发生在另一个脚本中,即
index.js
,我什至没有仔细查看它,因为它根本无法阅读:顺便说一句,这个脚本并不重要,因为我们可以使用简单的CSSs来防止样式更改.
现在 post 的内容已被“删除”,它看起来像这样:
*为了清楚起见:我添加了文本和墓碑,你实际上看不到它们 lol
所以,最后,当 post 离开视口时,变化如下:
<li class="post_container" [...]>
<div id="post_[id]" class="post post_full ..." [...]>
<!-- post body... -->
</div>
</li>
对此:
<li class="post_container" [...] style="border-radius: 3px; height: Hpx; width: Wpx; background-color: rgb(255, 255, 255);">
</li>
防止 Tumblr 代码删除 posts(确实是一些小技巧)
现在我们知道 Tumblr 在仪表盘中滚动时的行为方式,我们可以防止它删除 post 并将它们设置样式 看起来像空白的死块带有圆形边框。
风格第一
在关注防止 post 被删除之前,我们应该将它们设置为不具有固定的高度和宽度,覆盖 Tumblr 在它们离开视口时应用到它们的样式。
我们可以在页面中注入一些简单的 CSS,制定规则 !important
以防止内联样式覆盖它们。这是我们要添加的 CSS:
li.post_container {
height: auto !important;
width: 540px !important;
/* 540px is the default width
* change it to any amount that applies to your layout
*/
}
或者,使用 JavaScript,我们可以:
var s = document.createElement('style');
s.textContent = 'li.post_container{height:auto!important;width:540px!important;}';
document.head.appendChild(s);
防止 post 删除:简单(愚蠢)的方法
如果我们不想浪费时间思考一个好的解决方案,我们可以只实现最简单的一个,并且,在几行代码中我们可以:
复制
Element.prototype
的removeChild
方法,并将其命名为_removeChild
.将原来的替换为“过滤”对它的调用的函数,决定删除除posts之外的所有内容。
这是解决问题的简单但愚蠢的代码:
Element.prototype._removeChild = Element.prototype.removeChild
Element.prototype.removeChild = function(child) {
// Check if the element is a post (e.g. the id looks like "post_123456...")
if (/^post_\d+$/.test(child.id)) {
// If so, stop here by returning the post
// This will make Tumblr believe the post has been removed
return child;
}
// Otherwise the element isn't a post, so we can remove it
return Element.prototype._removeChild.apply(this, arguments);
}
无注释代码长度:5行
您显然可以检查多个条件:例如,您可以检查 post 是否包含 classes post
和 post_full
,或者它是否parentElement
有classpost_container
,等等...由你决定。
防止 post 删除:聪明(更难)的方法
完成此操作的一种更优雅但更巧妙的方法是“编辑”原始Element.prototype.removeChild
方法,使用一些闭包和更复杂的逻辑。这是我们要做的:
创建一个闭包,我们将在其中 重新定义
Element.prototype.removeChild
方法 为 post 添加一个“过滤器” s.在这个闭包中,我们将定义我们的自定义过滤器函数,我们可以调用它
isPost()
,以 过滤将要删除的元素.使用第二个闭包,我们将重新定义
Element.prototype.removeChild
方法,使其行为如下:检查要删除的元素 使用我们之前创建的过滤器函数
isPost()
.如果要移除的元素是post,则停止这里,不要移除。
否则继续并使用
.apply
方法调用原来的Element.prototype.removeChild
方法
看起来可能很难,但是代码并不难理解,我用注释完成了它,使它更容易理解,这里是:
(function (obj, method) {
// This is all wrapped inside a closure
// to make the new removeChild method remember the isPost function
// and maintain all the work invisible to the rest of the code in the page
function isPost(el) {
// Check if the element is a post
// This will return true if el is a post, otherwise false
return (/^post_\d+$/.test(el.id) && ~el.className.indexOf('post'));
}
// Edit the Element.prototype.removeChild assigning to it our own function
obj[method] = (function (obj_method) {
return function (child) {
// This is the function that will be assigned to removeChild
// It is wrapped in a closure to make the check before removing the element
// Check if the element that is going to be removed is a post
if (isPost(child)) return child;
// If so, stop here by returning the post
// This will make Tumblr believe the post has been removed
// Otherwise continue and remove it
return obj_method.apply(this, arguments);
}
}(obj[method]));
}(Element.prototype, 'removeChild'));
无注释代码长度:11行
在页面中注入脚本
由于 内容脚本在隐藏的、不同的 window
对象中工作,因此 内容脚本代码无法与页面代码交互,您必须从您的内容脚本直接在页面中注入代码。为此,您需要创建一个 <script>
元素并将其插入到页面的 <head>
中。您可以将要注入的代码存储在一个新脚本中(我们称之为 injectedCode.js
)并使用 chrome.runtime.getURL
获取绝对 URL 以在您的 <script>
src 中使用。
重要提示:您需要将 injectedCode.js
文件添加到您的 manifest.json
中的 "web_accessible_resources"
field 以使其可以从 Tumblr 访问,像这样:
"web_accessible_resources": ["/injectedCode.js"]
然后您可以创建一个 <script>
,将 .src
设置为您的 injectedCode.js
,如下所示:
/* In your content script: */
var s = document.createElement('script');
s.src = chrome.extension.getURL("/injectedCode.js");
document.head.appendChild(s);
尾注
这些只是完成您想要做的事情的两种方法:防止 Tumblr 在滚动时隐藏 posts。显然还有许多其他方法可以做到这一点,例如使用 MutationObserver
s 检查子树修改,删除所有 posts 并创建一个包含新个人事件和方法的新容器来模拟 Tumblr 的,插入Tumblr代码并直接编辑(几乎不可能,合法性也不高),等等。
我还想强调一个事实,这只是对一个问题的回答,我不负责 错误地使用此类操作来伤害您的扩展用户。