Nathan Snow
Nathan Snow

Reputation: 534

Why does HTML stored in localStorage change when recalled and displayed in the browser?

Sorry if this has been asked before, I've searched but couldn't find anything appropriately related...

I have a very simple site here that has 3 buttons:

  1. Add Component

  2. Save Current State

  3. Restore Saved State

The Add Component button does exactly what I want which is to create a new simple collapse item within the <div data-role="collapsible-set" class="flow" id="collapsibleComponent"></div> tags.

Collapsible component created and added to the page properly

Also, the Save Current State storeCurrentState(); button seems to capture the required code identically to what's being displayed (which is correct).

However, when I click the Restore Saved State button (run the restorePriorState(); method), the code that is read from the localStorage and put back into the page is not consistent with the code that was saved. As a result I end up with a nested looking collapsed item like below...

clustered mess

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>TutorialW3a</title>
        <link href="jquery-mobile/jquery.mobile.theme-1.3.0.min.css" rel="stylesheet" type="text/css" />
        <link href="jquery-mobile/jquery.mobile.structure-1.3.0.min.css" rel="stylesheet" type="text/css" />
        <script src="jquery-mobile/jquery-1.11.1.min.js"></script>
        <script src="jquery-mobile/jquery.mobile-1.3.0.min.js"></script>
    </head>
    <body>
        <div data-role="page" id="page">
            <div data-role="header">
                <h1>Header</h1>
            </div>
            <div data-role="content">
                <div data-role="collapsible-set" class="flow" id="collapsibleComponent">
                    <!--New collapsible divs go here-->
                </div>
            </div>
            <button onclick="addCollapsibleComponent()">Add component</button>
            <button onclick="storeCurrentState()">Save current state</button>
            <button onclick="restorePriorState()">Restore saved state</button>
            <div data-role="footer" data-position="fixed">
                <h4>Footer</h4>
            </div>
        </div>
    </body>
</html>
<script>
    //
    var collapsibleComponentContent = document.getElementById("collapsibleComponent");
    var collapsibleDiv;
    function addCollapsibleComponent() {
        //create div element
        collapsibleDiv = $('<div id="new" data-role="collapsible" data-collapsed="true"><h3>Heading</h3><p>Paragraph</p></div>"');
        //append new collapsible div to #collapsibleComponent
        $("#collapsibleComponent").append(collapsibleDiv);
        //collapse all collapsible divs
        $('div[data-role="collapsible"]').collapsible();
    }
    //
    function storeCurrentState() {
        collapsibleDiv = document.getElementById("new");
        localStorage.setItem("html_data", JSON.stringify(collapsibleComponentContent.innerHTML));
    }
    //
    function restorePriorState() {
        collapsibleComponentContent.innerHTML = JSON.parse(localStorage.getItem("html_data"));
        $("#new").collapsible();
    }
</script>

This is what is generated when a new collapsible component is created...

<div id="new" data-role="collapsible" data-collapsed="true" class="ui-collapsible ui-collapsible-inset ui-collapsible-collapsed">
<h3 class="ui-collapsible-heading ui-collapsible-heading-collapsed">
    <a href="#" class="ui-collapsible-heading-toggle ui-btn ui-btn-icon-left ui-btn-up-c" data-corners="false" data-shadow="false" data-iconshadow="true" data-wrapperels="span" data-icon="plus" data-iconpos="left" data-theme="c"
        ><span class="ui-btn-inner"
            ><span class="ui-btn-text">Heading<span class="ui-collapsible-heading-status"> click to expand contents</span></span
            ><span class="ui-icon ui-icon-plus ui-icon-shadow">&nbsp;</span></span
        ></a
    >
</h3>
<div class="ui-collapsible-content ui-collapsible-content-collapsed" aria-hidden="true"><p>Paragraph</p></div>

And this is what is created when I reload...

<h3 class="ui-collapsible-heading ui-collapsible-heading-collapsed">
<a href="#" class="ui-collapsible-heading-toggle ui-btn ui-btn-icon-left ui-btn-up-c" data-corners="false" data-shadow="false" data-iconshadow="true" data-wrapperels="span" data-icon="plus" data-iconpos="left" data-theme="c"
    ><span class="ui-btn-inner"
        ><span class="ui-btn-text"
            ><a href="#" class="ui-collapsible-heading-toggle ui-btn ui-btn-icon-left ui-btn-up-c" data-corners="false" data-shadow="false" data-iconshadow="true" data-wrapperels="span" data-icon="plus" data-iconpos="left" data-theme="c"
                ><span class="ui-btn-inner"
                    ><span class="ui-btn-text">Heading<span class="ui-collapsible-heading-status"> click to expand contents</span></span
                    ><span class="ui-icon ui-icon-plus ui-icon-shadow">&nbsp;</span></span
                ></a
            ><span class="ui-collapsible-heading-status"> click to expand contents</span></span
        ><span class="ui-icon ui-icon-plus ui-icon-shadow">&nbsp;</span></span
    ></a
>

Upvotes: 1

Views: 134

Answers (1)

deblocker
deblocker

Reputation: 7707

You are asking "Why", so here is the answer:

JQM is a mixed JS/CSS framework, i.e. the widgets are enhanced by JS and styled by CSS. After page initialization, the widgets (identified by the "data-role" tag) are enhanced by the framework by adding some nested elements (div, span), pseudo-elements (icons) and event handlers.

That said, in Your code You are saving the enhanced version of Your initial markup together with the data-role attribute, but You are saving only the markup, this means You won't save the JS data which will be created by the framework during the widget initialization. In short, You are saving only one half of the already enhanced widget.

The next time, the framework will find the data-role attribute again, but not the associated data object and by invoking $("#new").collapsible(); You will end up with that double-nested markup.

More information:

You can take a look at the JQM widget object as follows:

$.data(document.getElementById("new"),"mobile-collapsible")

or:

$("#new").jqmData("mobile-collapsible")

There is more than one possible solution for Your task.

Maybe You can restore the initial un-enhanced markup:

$("#collapsibleComponent :jqmData(role='collapsible')").collapsible("destroy");

and save that:

localStorage.setItem("html_data", JSON.stringify($("#collapsibleComponent").html()));

Finally, You assumption that

to capture the required code identically to what's being displayed is correct

is wrong.

Upvotes: 1

Related Questions