如何在受到javascript影响后返回原始DOM

时间:2014-07-10 07:58:05

标签: javascript php jquery dom

想象一下,我有一个加载的HTML页面已经受到javascript在初始化时添加/删除动态元素或新类/属性/ id元素的影响(例如:原始源代码[html]标签没有类,在javascript加载后[html]标签有class =“no-responsive full-with”)。想象一下,之后我手动添加/修改一些id值(通过我的应用程序)。并且想象一下,我需要能够在数据库中保存原始源代码(没有任何修改),但我手动添加了id属性。

基本上我需要将一个给定的id属性添加到HTML源代码中的一个元素,通过PHP加载。

你们有没有想过如何做这样的事情?

1 个答案:

答案 0 :(得分:3)

这里没有简单的解决方案。复杂解决方案的确切性质将取决于您的全部要求。

更新概念

你已经说过,除了改变事物之外,你还会添加元素并删除它们。所以你不能纯粹在结构上将改变后的元素与原文相关联(例如,通过子索引),因为那些可能会改变。

所以这就是我可能会接近它的方式:

在加载页面之后,进行任何修改之前,立即给出唯一标识符中的每个元素。使用jQuery非常简单(没有它也不会特别难):

var uniqueId = 0;
$("*").attr("data-uid", function() {
    return ++uniqueId;
});

现在,页面上的每个元素都有唯一的标识符。接下来,复制DOM并获取它的jQuery包装器:

var clone = $("html").clone();

现在,您可以通过唯一ID将DOM中的元素与其原始版本(我们的克隆)相关联。允许用户进行更改。

当您准备好了解所做的更改时,请执行以下操作:

// Look for changes
clone.find("*").addBack().each(function() {
    // Get this clone's unique identifier
    var uid = $(this).attr("data-uid");

    // Get the real element corresponding to it, if it's
    // still there
    var elm = $("[data-uid=" + uid + "]")[0];

    // Look for changes
    if (!elm) {
        // This element was removed
    }
    else {
        if (elm.id !== this.id) {
            // This element's id changed
        }
        if (elm.className !== this.className) {
            // This element's className changed
        }
        // ...and so on...
    }
});

这将告诉您有关删除和更改的元素的信息。如果您还想查找添加的元素,请执行以下操作:

var added = $(":not([data-uid])");

...因为他们没有属性。

您可以使用clone中的信息重建原始DOM的字符串:

clone.find("[data-uid]").addBack().removeAttr("data-uid");
var stringToSend = clone[0].outerHTML;

(任何模糊的现代浏览器都支持outerHTML,最新添加的是v11中的Firefox。)

...当然还有以上信息来记录变化。

Live proof of concept

HTML:

<p class="content">Some content</p>
<p class="content">Some further content</p>
<p>Final content</p>
<input type="button" id="makeChange" value="Make Change">
<input type="button" id="seeResults" value="See Results">

JavaScript的:

// Probably unnecessary, but I wanted a scoping
// function anyway, so we'll give the parser time
// to completely finish up.
setTimeout(function() {
    // Assign unique identifer to every element
    var uniqueId = 0;
    $("*").attr("data-uid", function() {
        return ++uniqueId;
    });

    // Clone the whole thing, get a jQuery object for it
    var clone = $("html").clone();

    // Allow changes
    $("#makeChange").click(function() {
        this.disabled = true;
        $("p:eq(1)").attr("id", "p1");
        $("p:eq(2)").addClass("foo");
        alert("Change made, set an id on one element and added a class to another");
    });

    // See results
    $("#seeResults").click(function() {
        this.disabled = true;

        // Look for changes
        clone.find("*").addBack().each(function() {
            // Get this clone's unique identifier
            var uid = $(this).attr("data-uid");

            // Get the real element corresponding to it, if it's
            // still there
            var elm = $("[data-uid=" + uid + "]")[0];

            // Look for changes
            if (!elm) {
                display("Element with uid " + uid + ": Was removed");
            }
            else {
                if (elm.id !== this.id) {
                    display("Element with uid " + uid + ": <code>id</code> changed, now '" + elm.id + "', was '" + this.id + "'");
                }
                if (elm.className !== this.className) {
                    display("Element with uid " + uid + ": <code>className</code> changed, now '" + elm.className + "', was '" + this.className + "'");
                }
            }
        });
    });

    function display(msg) {
        $("<p>").html(String(msg)).appendTo(document.body);
    }
}, 0);

早期答案

假设服务器每次询问时都会为页面提供相同的文本,您可以通过ajax获取未更改的文本客户端。这给我们留下了如何将id属性应用于它的问题。

如果您需要原始内容但必然是相同的来源(例如,如果标签名称更改,则[div可能会变为DIV],或属性获得/丢失它们周围的引号),您可以使用服务器中的源(通过ajax检索)来填充文档片段,并在将片段应用于主文档的同时将id值应用于片段。然后将片段的源发送到服务器。

使用服务器中的完整HTML填充片段并不是那么容易。假设html没有任何类或任何内容,那么:

var frag, html, prefix, suffix;
frag = document.createDocumentFragment();
html = document.createElement("html");
frag.appendChild(html);
prefix = stringFromServer..match(/(^.*<html[^>]*>)/);
prefix = prefix ? prefix[1] : "<!doctype html><html>";
suffix = stringFromServer.match(/(<\/html>\s*$)/);
suffix = suffix ? suffix[1] : "</html>";
html.innerHTML = stringFromServer.replace(/^.*<html[^>]*>/, '').replace(/<\/html>\s*$/, '');

在那里,我们获取服务器的字符串,抓取最外面的HTML部分(或使用默认值),然后将内部HTML分配给片段内的html元素(尽管我想的越多,越少我看到需要一个片段 - 你可能只是删除片段部分)。 (旁注:上面正则表达式中标识html元素<html[^>]*>的开始标记的部分是“足够好”的东西之一。它并不完美,特别是如果你在引用的属性值中有一个>会失败,如下所示:<html data-foo="I have a > in me">,这是完全有效的。解决这个问题需要更难解析,所以我假设你没有这样做,因为它很不寻常。)

然后,您可以通过html.querySelectorhtml.querySelectorAll找到其中的元素,以便将id属性应用于它们。形成相关的选择器将是非常有趣的,可能是很多位置的东西。

完成后,返回要发送到服务器的HTML,如下所示:

var stringToSend = prefix + html.innerHTML + suffix;