Search code examples
javascripthtmlclone

Why elements after restoration loses their offset between each other?


I am trying to implement bookmarks thing for my site and testing this:

<html>
    <head>
        <title>test bookmark</title>
        <script type='text/javascript' src='libs/bookmarks.js'></script>
    </head>
    <body>
        <input type = "text" id = "txt_id" />
        <input type = "button" value = "create bk" onclick = "createBookmark();" />
        <input type = "button" value = "restore bk 0" onclick = "restoreBookmarkNumber(0);" />
    </body>
</html>

bookmarks.js:

function Bookmark(of_elem, name)
{
    this.name = name;
    this.of_elem = of_elem;    
    this.elems = new Array();

    for (var i = 0; i < of_elem.children.length; i++)
        this.elems.push(of_elem.children[i].cloneNode(true));

    return this;
};

Bookmark.prototype.restore = function ()
{
    while (this.of_elem.hasChildNodes())
        this.of_elem.removeChild(this.of_elem.lastChild);

    for (var i = 0; i < this.elems.length; i++)
        this.of_elem.appendChild(this.elems[i].cloneNode(true));
};

var bookmarks = new Array();

function createBookmark()
{
    var name = prompt("Enter bookmark name", "Bookmark " + bookmarks.length);
    var bk = new Bookmark(document.body, name);
    bookmarks.push(bk);
}

function restoreBookmarkNumber(num)
{
    var bk = bookmarks[num];

    if (bk)
        bk.restore();
}

For some weird reason, the space between textbox and buttons disappears after bookmark restoration, but I don't understand why this happens. I clearly clone all elements, so what else it need?enter image description here


Solution

  • In the places you're using children rather than childNodes (such as the Bookmark constructor), you're only dealing with element children (since that's the point of children vs. childNodes). Use childNodes throughout to ensure you maintain text nodes between elements, since text nodes between elements can be significant (e.g., for spacing).

    Your original, which loses the spacing:

    function Bookmark(of_elem, name)
    {
      this.name = name;
      this.of_elem = of_elem;    
      this.elems = new Array();
    
      for (var i = 0; i < of_elem.children.length; i++)
        this.elems.push(of_elem.children[i].cloneNode(true));
    
      return this;
    };
    
    Bookmark.prototype.restore = function ()
    {
      while (this.of_elem.hasChildNodes())
        this.of_elem.removeChild(this.of_elem.lastChild);
    
      for (var i = 0; i < this.elems.length; i++)
        this.of_elem.appendChild(this.elems[i].cloneNode(true));
    };
    
    var bookmarks = new Array();
    
    function createBookmark()
    {
      var name = prompt("Enter bookmark name", "Bookmark " + bookmarks.length);
      var bk = new Bookmark(document.body, name);
      bookmarks.push(bk);
    }
    
    function restoreBookmarkNumber(num)
    {
      var bk = bookmarks[num];
    
      if (bk)
        bk.restore();
    }
    <html>
        <head>
            <title>test bookmark</title>
            <script type='text/javascript' src='libs/bookmarks.js'></script>
        </head>
        <body>
            <input type = "text" id = "txt_id" />
            <input type = "button" value = "create bk" onclick = "createBookmark();" />
            <input type = "button" value = "restore bk 0" onclick = "restoreBookmarkNumber(0);" />
        </body>
    </html>

    Updated to use childNodes instead of children in Bookmark, which doesn't lose the spacing:

    function Bookmark(of_elem, name)
    {
      this.name = name;
      this.of_elem = of_elem;    
      this.elems = new Array();
    
      for (var i = 0; i < of_elem.childNodes.length; i++)
        this.elems.push(of_elem.childNodes[i].cloneNode(true));
    
      return this;
    };
    
    Bookmark.prototype.restore = function ()
    {
      while (this.of_elem.hasChildNodes())
        this.of_elem.removeChild(this.of_elem.lastChild);
    
      for (var i = 0; i < this.elems.length; i++)
        this.of_elem.appendChild(this.elems[i].cloneNode(true));
    };
    
    var bookmarks = new Array();
    
    function createBookmark()
    {
      var name = prompt("Enter bookmark name", "Bookmark " + bookmarks.length);
      var bk = new Bookmark(document.body, name);
      bookmarks.push(bk);
    }
    
    function restoreBookmarkNumber(num)
    {
      var bk = bookmarks[num];
    
      if (bk)
        bk.restore();
    }
    <html>
        <head>
            <title>test bookmark</title>
            <script type='text/javascript' src='libs/bookmarks.js'></script>
        </head>
        <body>
            <input type = "text" id = "txt_id" />
            <input type = "button" value = "create bk" onclick = "createBookmark();" />
            <input type = "button" value = "restore bk 0" onclick = "restoreBookmarkNumber(0);" />
        </body>
    </html>