Search code examples
javascripthtmliframelocal-storageautosave

Autosaving (localStorage) textfield from iFrame doesn't work


I am building a rich text editor and want to autosave the text in the iframe. I have multiple code parts and snippets which – taken by itself – are working perfectly but I am failing to assemble them correctly. LIVEDEMO

Here is a graphic of what I want to achieve: Editor Structure

I have an editable iFrame which is my text editor. The innerHTML of this iFrame should be loaded into a textarea hidden under the iframe and the content of the textarea should be saved in the localStorage regularly.

The iFrame code works.

<script>

  function iFrameOn() {
    richTextField.document.designMode = 'On';
  }

  function iBold() {
    richTextField.document.execCommand('bold', false, null);
  }

  function iUnderline() {
    richTextField.document.execCommand('underline', false, null);
  }

  function iItalic() {
    richTextField.document.execCommand('italic', false, null);
  }

  function iFontSize() {
    var size = prompt('Enter a size 1-7', '')
    richTextField.document.execCommand('FontSize', false, size);
  }

  function iForeColor() {
    var color = prompt('Define a basic color or apply a hexadecimal color code for advanced colors:', '')
    richTextField.document.execCommand('ForeColor', false, color);
  }

  function iHorizontalRule() {
    richTextField.document.execCommand('InsertHorizontalRule', false, null);
  }

  function iUnorderedList() {
    richTextField.document.execCommand('InsertUnorderedList', false, "newUL");
  }

  function iOrderedList() {
    richTextField.document.execCommand('InsertOrderedList', false, "newOL");
  }

  function iLink() {
    var linkURL = prompt('Enter the URL for this link:', 'http://')
    richTextField.document.execCommand('CreateLink', false, linkURL);
  }

  function iUnLink() {
    richTextField.document.execCommand('UnLink', false, null);
  }

  function iImage() {
    var imgSrc = prompt('Enter image location:', '');
    if(imgSrc != null){
    richTextField.document.execCommand('insertimage', false, imgSrc);
    }
  }

</script>

<body onLoad="iFrameOn();">
  <h2>Text Editor</h2>
  <form action="my_parse_file.php" name="myform" id="myform" method="post" onLoad="autosave_form();">
        <fieldset>
        <p>Entry Title: <input name="title" id="title" type="text" size="80" maxlength="80" /></p>
        <p>Entry Body:<br />

            <div id="wysiwyg_cp" style="padding: 8px; width: 700px;">

                <input type="button" onClick="iBold()" value="B" />
                <input type="button" onClick="iUnderline()" value="U" />
                <input type="button" onClick="iItalic()" value="I" />
                <input type="button" onClick="iFontSize()" value="Text Size" />
                <input type="button" onClick="iForeColor()" value="Text Color" />
                <input type="button" onClick="iHorizontalRule()" value="HR" />
                <input type="button" onClick="iUnorderedList()" value="UL" />
                <input type="button" onClick="iOrderedList()" value="OL" />
                <input type="button" onClick="iLink()" value="Link" />
                <input type="button" onClick="iUnLink()" value="UnLink" />
                <input type="button" onClick="iImage()" value="Image" />

            </div>

            <textarea style="display: none;" name="myTextArea" id="myTextArea" cols="100" rows="14"></textarea>
            <iframe name="richTextField" id="richTextField" style="border: #000000 1px solid; width: 700px; height: 300px"></iframe>


        </p><br />
        <input name="myBtn" type="button" value="Submit Data" onClick="javascript:submit_form();"/>
        </fieldset>
</form>
</body>

The autosaver also works – but only with the textarea and not the iFrame.

// get the state of the form
function getFormState() {
  var fields = document.getElementsByTagName('form')[0].elements;
  if (fields.length == 0){return};
  for (var i = 1; i <= fields.length-1; i++) {
    var name = fields[i].getAttribute('name');
    if (name in localStorage && localStorage[name] !== null) {
        fields[i].value = localStorage[name];
    }
  }
}

// save the state of the form
function saveFormState() {
  var fields = document.getElementsByTagName('form')[0].elements;
  if (fields.length == 0){return};
  var populated = false;
  for (var i = 1; i <= fields.length-1; i++) {
    var name = fields[i].getAttribute('name');
    if (fields[i].value != '' && fields[i].getAttribute('type') != 'submit') {
       localStorage[name] = fields[i].value;
       populated = true;
    }
  }

  // display the time form data was saved (optional)
  if (populated) {
    var date = new Date();
    var hours = date.getHours();
    var mins = date.getMinutes();
    var secs = date.getSeconds();
    hours = (hours < 10) ? '0' + hours : hours;
    mins = (mins < 10) ? '0' + mins : mins;
    secs = (secs < 10) ? '0' + secs : secs;
    var msg = '[Form data was saved at ' + hours + ':' + mins + ':' + secs + ']';
    var timecont = document.getElementById('time_container');
    if (timecont !== null) {
        timecont.innerHTML = msg;
    }
    else {
        timecont = document.createElement('span');
        timecont.setAttribute('id', 'time_container');
        timecont.appendChild(document.createTextNode(msg));
        document.getElementsByTagName('fieldset')[0].appendChild(timecont);
    }
  }
}

// run the above functions when the web page is loaded
window.onload = function () {
  // check if HTML5 localStorage is supported by the browser
  if ('localStorage' in window && window['localStorage'] !== null) {
    // get the form state
    getFormState();
    // save the state of the form each 15 seconds (customizable)
    setInterval('saveFormState()', 15 * 1000);
  }
}

And now I need some binding code which loads the innerHTML of the iFrame routinely into the textarea.

function autosave_form() {
  var theForm = document.getElementbyID('myform');
  theForm.elements["myTextArea"].value = window.frames['richTextField'].document.body.innerHTML;
  setTimeout(yourFunction, 5000);
}

So where are my fellacies? Or what did I overlook? Please help. LIVEDEMO


Solution

  • You're not using the interface on the iframe object correctly. To get the content of an iframe on the same domain, you need to go through the contentWindow field. For example:

    function read_iframe() {
      return window.frames['richTextField'].contentWindow.document.body.innerHTML;
    }
    

    Remember that you cannot read the content of an iframe on another domain.

    You can find complete docs on the HTMLIFrameElement's methods and fields at https://developer.mozilla.org/en/docs/Web/API/HTMLIFrameElement