Skip to content

Instantly share code, notes, and snippets.

@megawac
Last active January 1, 2016 20:59
Show Gist options
  • Save megawac/8201012 to your computer and use it in GitHub Desktop.
Save megawac/8201012 to your computer and use it in GitHub Desktop.
Saving the state of an element and its children.

Problem

A problem I ran into implementing my MutationObserver shimmed interface is being able to save the state of an elements children for later use. Naively I started by just using $oldnode = $node.cloneNode(true) which would give me an atomic node at a previous state. The problem with using cloneNode is when it comes time to compare. Any children in $oldnode will be !== to the same child in $node.

I addressed this issue by assigning a unique id to all elements and checking if the unique id of an old node is the same as from an earlier state. This is ugly and noone wants me adding my own unique id property on their elements.

var counter = 0;
var getId = function($ele) {
    var id = $ele.nodeType === 3 ? $ele.nodeValue ://text node id is the text content
                                    $ele.id || $ele.getAttribute("mut-id") || ++counter;
    if(id === counter) {
        $ele.setAttribute("mut-id", id);
    }
    return id;
};

Solution?

I'm considering representing an elements clone in a custom datastructure: probably an array of hashes. Each clone will be an array of objects with a key ele which stores the real element and a key childs which will store the state of the elements childNodes (in an array because nodelists are live) from a previous point of time.

Here's my naive clone implementation:

var map = Array.prototype.map;
var clone = function (children,deep) {
    return map.call(children, function(node) {
        return {
            node: node.nodeType === 3 ? node.cloneNode(false) : node,//clone in case of text changes
            children: deep && node.hasChildNodes() ? clone(node.childNodes, deep) : null
        };
    });
};

So for the following node being held by the variable $node would be cloned to :

<ul class="test">
    <li>
        <span>text</span>
        <p>stuff<strong>10</strong><i>of</i><span>10</span></p>
    </li>
</ul>
child_clone(node.childNodes, false) => [{"node":"<li><span>text</span><p>stuff<strong>10</strong><i>of</i><span>10</span></p></li>","children":null}]

Benefits ?

Cloning will probably slower but iterating will be faster as === will work in place of my sameNode helper. Adding items when there are changes may be awkward. It'd probably be most efficient to compare my datastructure to the current state of the node to reduce the need for cloning.

Intuitively I believe my datastructure will be more memory efficent as nodes aren't cloned and are just pointers?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment