简体   繁体   English

防止删除Tumblr仪表板上的DOM元素

[英]Prevent removal of DOM elements on the Tumblr dashboard

NOTE: This is a follow-up question to my previous one . 注意:这是我之前的一个后续问题。

I decided to keep the posts displayed on the dashboard by my extension inside the #posts element. 我决定通过#posts元素内的扩展程序将帖子显示在仪表板上。 Now my problem is that Tumblr automatically removes posts' content while scrolling . 现在我的问题是Tumblr在滚动时自动删除帖子的内容

Here is an example of a normal post before the removal : 以下是删除前正常帖子的示例:

<li class="post_container" data-pageable="post_110141415125">
    <div class="post post_full ..." [...]>
        <!-- ... -->
    </div>
</li>

And the same post, after the removal of its content: 删除其内容后,同一帖子:

<li class="post_container" data-pageable="post_110141415125" style="border-radius: 3px; height: 207px; width: 540px; background-color: rgb(255, 255, 255);">
</li>

Since that my extension re-positions the posts into a pinterest-like layout, the removal of posts' content messes up the entire layout . 由于我的扩展程序将帖子重新定位为类似于pinterest的布局, 因此删除帖子的内容会影响整个布局 Is it possible to stop the Tumblr dashboard from removing the DOM elements inside each .post ? 是否可以阻止Tumblr仪表板删除每个.post的DOM元素?

EDIT: 编辑:

I added this injection code for the removeChild method in my content script: 我在我的内容脚本中为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;

The next I did, was to create the injectedCode.js file with the second version, looking like this: 接下来我做的是用第二个版本创建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'));

When I load the Tumblr-Dashboard I can confirm that the Script-element is present in the head-element. 当我加载Tumblr-Dashboard时,我可以确认head-element中存在Script-element。 The file is also properly linked, because the 'removeChild' function is called once. 该文件也正确链接,因为'removeChild'函数被调用一次。 A script element is being removed there, it seems. 似乎正在删除脚本元素。

From that point on, the function is never called again and I can see those 'empty' posts again. 从那时起,该函数永远不会被再次调用,我可以再次看到那些“空”的帖子。

Hijacking Tumblr dashboard behavior 劫持Tumblr仪表板行为

Disclaimer : completely legal things going on here, don't panic! 免责声明 :完全合法的事情发生在这里,不要惊慌!

What you're asking is something that is not really easy to do, but you can actually stop Tumblr from hiding the posts while scrolling when they get out of the viewport. 你要问的是一些不容易做的事情,但实际上你可以阻止Tumblr在他们离开视口时滚动时隐藏帖子。 More precisely, an advanced programmer could actually manipulate any Tumblr method to make it behave like they want, but, for now, let's focus on your question. 更确切地说,一个高级程序员实际上可以操纵任何Tumblr方法使其表现得像他们想要的那样,但是,现在,让我们关注你的问题。

Why does Tumblr remove posts while scrolling? 为什么Tumblr会在滚动时删除帖子?

Tumblr removes posts' content to make the page lighter and let you continue browsing without experiencing any lag and/or struggle scrolling because of the enormous amount of elements. Tumblr删除帖子的内容以使页面更轻 ,让您继续浏览,而不会因为大量元素而遇到任何滞后和/或难以滚动。 Imagine scrolling down for an hour straight: your dash would hold thousands of posts, and your browser will probably freeze due to the excessive amount of things that it has got to move each time you scroll. 想象一下直接向下滚动一小时:你的破折号会有数千个帖子,你的浏览器可能会因每次滚动时必须移动的东西过多而冻结。

How does Tumblr accomplish this? Tumblr如何实现这一目标?

I didn't inspect and understand every single line of the Tumblr code, because it's all packed/minified, and it would be impossible to actually understand a single line of any method (unless you work at Tumblr, in which case congratulations), but, basically, I put some DOM breakpoints and I've inspected the part of code that removes the posts when they go out of the viewport. 我没有检查和理解Tumblr代码的每一行,因为它全部被打包/缩小,并且实际上不可能理解任何方法的单行(除非你在Tumblr工作,在这种情况下祝贺),但是,基本上,我放了一些DOM断点 ,我检查了一些代码,当它们离开视口时删除了帖子。

In a nutshell, this is what it does: 简而言之,这就是它的作用:

  1. Tumblr creates some custom and crafty "scroll" events for the posts. Tumblr为帖子创建了一些自定义和狡猾的“滚动”事件。 These events will fire every time a post gets more than a certain distance out of the viewport . 每次帖子超出视口一定距离时,这些事件都会触发。

  2. Now Tumblr attaches some listeners to these custom events, and, using a DOM breakpoint on a li.post_container element , you'll be able to see that these listeners reside in the dashboard.js JavaScript file. 现在Tumblr将一些侦听器附加到这些自定义事件,并且li.post_container元素上使用DOM断点 ,您将能够看到这些侦听器驻留在dashboard.js JavaScript文件中。

    dashboard.js

    More precisely, the exact part of code that removes the posts' content looks like this: 更确切地说,删除帖子内容的代码的确切部分如下所示:

     function j(t,v) { try{ t.removeChild(v) } catch(u){} } 

    where t is the li.post_container and v is its children: div#post_[id] . 其中tli.post_containerv是它的子div#post_[id]div#post_[id]

    What does this part of code do? 这部分代码有什么作用? Nothing special: it literally removes the div#post_[id] element from its parent li.post_container . 没有什么特别的:它确实删除div#post_[id]从它的父元素li.post_container

  3. Now Tumblr has got to style the removed post's container in order to maintain the height of the #posts element, which holds all the posts, otherwise removing a post's content would make the page to scroll down H pixels, where H is the height of the post which got removed. 现在Tumblr必须设置已删除帖子的容器的样式以保持#posts元素的高度,该元素保存所有帖子,否则删除帖子的内容会使页面向下滚动H像素,其中H是高度被删除的帖子。

    This change takes place inside another script, which is index.js , and I didn't even take a closer look at it because it's just impossible to read: 这个更改发生在另一个脚本,即index.js ,我甚至没有仔细看看它,因为它只是不可能阅读:

    index.js

    By the way, this script is not important , since that we can use simple CSSs to prevent style changes . 顺便说一句, 这个脚本并不重要 ,因为我们可以使用简单的CSS来防止样式更改

  4. Now that the post's content has been "removed", it looks something like this: 现在该帖子的内容已被“删除”,它看起来像这样:

    删除帖子

    *Just for clarity: I added the text and the tombstone, you don't actually see them lol *为了清楚起见:我添加了文本和墓碑,你实际上没有看到它们哈哈

So, in the end, when a post gets out of the viewport, changes from this: 所以,最后,当一个帖子离开视口时,会发生以下变化:

<li class="post_container" [...]>
    <div id="post_[id]" class="post post_full ..." [...]>
        <!-- post body... -->
    </div>
</li>

to this: 对此:

<li class="post_container" [...] style="border-radius: 3px; height: Hpx; width: Wpx; background-color: rgb(255, 255, 255);">
</li>

Prevent Tumblr code from removing posts (some little hack indeed) 防止Tumblr代码删除帖子(确实有些小黑客)

Now that we know how Tumblr behaves when scrolling through the dashboard, we can prevent it from removing posts and styling them to look like empty white dead blocks with rounded borders. 现在我们知道Tumblr在滚动仪表板时的行为,我们可以阻止它删除帖子并将它们设计为看起来像带有圆形边框的空白色死区块。

Style first 风格第一

Before caring about preventing the posts from being removed, we should style them not to be of fixed height and width, overriding the styles applied to them by Tumblr when they go out of the viewport. 在关心防止移除柱子之前,我们应该将它们设置为不具有固定的高度和宽度,从而覆盖Tumblr离开视口时应用于它们的样式。

We can do this injecting some simple CSSs in the page, making the rules !important to prevent inline styles from overriding them. 我们可以在页面中注入一些简单的CSS,使规则!important以防止内联样式覆盖它们。 Here's the CSS we'll add: 这是我们要添加的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 
     */
}

Alternatively, using JavaScript, we can do: 或者,使用JavaScript,我们可以:

var s = document.createElement('style');
s.textContent = 'li.post_container{height:auto!important;width:540px!important;}';
document.head.appendChild(s);

Prevent post removal: easy (silly) way 防止移除后:轻松(愚蠢)的方式

If we do not want to lose time thinking of a good solution, we could just implement the simplest one, and, In a few lines of code we could: 如果我们不想浪费时间考虑一个好的解决方案,我们可以实现最简单的解决方案,并且,在几行代码中我们可以:

  1. Duplicate the removeChild method of the Element.prototype , and call it something like _removeChild . 复制Element.prototyperemoveChild方法,并将其称为_removeChild

  2. Replace the original one with a function that "filters" the calls made to it, deciding to remove anything except posts. 用一个 “过滤”调用它的函数替换原来的函数,决定删除除帖子之外的任何内容。

Here's the simple yet silly code to solve the issue: 这是解决问题的简单而愚蠢的代码:

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);
}

Code length without comments: 5 lines 没有注释的代码长度:5行

You can check for more than one condition obviously: for example you can check if the post contains the classes post and post_full , or if it's parentElement has class post_container , and so on... it's up to you. 您可以明显检查多个条件:例如,您可以检查帖子是否包含postpost_full类,或者它的parentElement是否具有类post_container ,依此类推......这取决于您。

Prevent post removal: smart (harder) way 防止移除后:聪明(更难)的方式

A more elegant, yet crafty, way to accomplish this manipulation is to "edit" the original Element.prototype.removeChild method , using some closures and more complicated logic. 完成此操作的一种更优雅,更狡猾的方法是使用一些闭包和更复杂的逻辑“编辑”原始的Element.prototype.removeChild方法 Here's what we're going to do: 这就是我们要做的事情:

  1. Create a closure in which we'll redefine the Element.prototype.removeChild method adding a "filter" to it for the posts. 创建一个闭包,我们将在其中重新定义Element.prototype.removeChild方法 ,为帖子添加“过滤器”。

  2. In this closure we will define our custom filter function, which we could call isPost() , to filter the elements that are going to be removed . 在这个闭包中,我们将定义我们可以调用isPost()自定义过滤器函数来过滤要删除的元素

  3. Using a second closure, we'll redefine the Element.prototype.removeChild method to behave like this: 使用第二个闭包,我们将重新定义Element.prototype.removeChild方法,使其行为如下:

    1. Check the element to remove using our previously created filter function isPost() . 使用我们之前创建的过滤函数isPost() 检查要删除的元素

    2. If the element to remove is a post, then stop here and do not remove it. 如果要删除的元素是帖子,则在此处停止 ,不要将其删除。

    3. Otherwise continue and call the original Element.prototype.removeChild method using the .apply method. 否则, 继续并使用.apply方法调用原始Element.prototype.removeChild方法。

It may look difficult, but the code is not that hard to understand, and I fulfilled it with comments to make it more comprehensible, here it is: 它可能看起来很难,但代码并不难理解,我通过评论来实现它以使其更易于理解,这里是:

(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'));

Code length without comments: 11 lines 没有注释的代码长度:11行

Injecting the script in the page 在页面中注入脚本

Since that the content scripts work in an hidden, different window object , and therefore content script code cannot interact with page code , you'll have to inject the code directly in the page from your content script. 由于内容脚本在隐藏的不同window对象中工作 ,因此内容脚本代码无法与页面代码交互 ,因此您必须直接在内容脚本的页面中注入代码 To do this you'll need to create a <script> element and insert it in the <head> of the page. 为此,您需要创建一个<script>元素并将其插入页面的<head>中。 You can store the code you want to inject in a new script (let's call it injectedCode.js ) and use chrome.runtime.getURL to get the absolute URL to use in your <script> src. 你可以存储你想在一个新的脚本注入代码(姑且称之为injectedCode.js ),并使用chrome.runtime.getURL得到绝对URL在您使用<script>的src。

Important : you'll need to add the injectedCode.js file to your "web_accessible_resources" field in your manifest.json to make it accessible from Tumblr, like this: 重要提示 :你需要的添加injectedCode.js文件到您的"web_accessible_resources"字段中的manifest.json ,使其从访问的tumblr,就像这样:

"web_accessible_resources": ["/injectedCode.js"]

Then you can create a <script> setting the .src to your injectedCode.js like this: 然后,你可以创建一个<script>设定.srcinjectedCode.js是这样的:

/* In your content script: */

var s = document.createElement('script');
s.src = chrome.extension.getURL("/injectedCode.js");
document.head.appendChild(s);

End-notes 最终笔记

These are just two methods to accomplish what you want to do: prevent Tumblr from hiding posts while scrolling. 这些只是实现您想要做的两种方法:防止Tumblr在滚动时隐藏帖子。 There obviously are many other ways to do this, such as using MutationObserver s to check for subtree modifications, remove all the posts and create a new container with new personal events and methods to emulate the Tumblr ones, interject the Tumblr code and edit it directly (almost impossible and not much legit), and so on. 显然还有很多其他方法可以做到这一点,例如使用MutationObserver检查子树修改,删除所有帖子并创建一个新的容器,其中包含新的个人事件和方法来模拟Tumblr,插入Tumblr代码并直接编辑它(几乎不可能,也不太合法),等等。

I also want to underline the fact that this is only an answer to a question , and I'm not responsible for an incorrect use of these kind of manipulations to harm your extension's users . 我还想强调一个事实, 这只是一个问题的答案我不负责不正确地使用这些操作来伤害你的扩展程序的用户

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM