Search code examples
javascriptjqueryiframetextarealiveedit

Live editor that allows for editing of an iframe's HTML and CSS within a textarea (or similar.)


Okay, to preface, I am not an overly experience programmer. My area is predominantly in HTML and CSS, so I'm a little stuck here, and I've searched high and low online and can't find what I need; whether that's bad phrasing on my part or lack of information, I'm not sure, haha.

THE LIVE VERSION

HTML
<select ONCHANGE="document.getElementById('display-inner').src = this.options[this.selectedIndex].value"> <option value="http://tessisamess.com/freeedit.html">Free Edit</option> <option value="http://tessisamess.com/completestyle.html">Complete Style</option> <option value="http://tessisamess.com/generator.html">Generator</option>

JQUERY

    $(function() {
    function change() {
    var iFrame =  document.getElementById('display-inner');
    var iFrameBody;
    if ( iFrame.contentDocument ) 
    { // FF
    iFrameBody = iFrame.contentDocument.getElementsByTagName('body')[0];
    }
    else if ( iFrame.contentWindow ) 
    { // IE
    iFrameBody = iFrame.contentWindow.document.getElementsByTagName('body')[0];
    }
    iFrameBody.innerHTML = document.getElementById('input-inner').value;
    }
    });

What I'm trying to accomplish:
I'm creating a live editor for the users that I generally create layouts for, and I've got Free Edit mode and the Save function all in working order. I've also got my two base layouts that are often restyled uploaded and displayed in iframes, and you can call either layout or Free Edit from a dropdown to display on the right, next to the editor.

The goal here is for you to be able to click any of the three modes, and then code on the left and see your updates effect what's already in the iframe to the right rather than clearing it and replacing it with your textarea content (which is what it currently does.)

What needs to function, and doesn't:
When you click Complete Style or Generator, the textarea (left) needs to display the base code that people use to style each layout. When you start editing that side, either by using the code generated for you, or replacing it with a premade layout edit, the changes effect the iframe it's connected to on the right. (example: Adding a background image to body will change the page background in the iframe, and so on.)

The purpose:
I'm using this as a tool to give the users who frequently use my free layouts on the site, who have trouble editing them using the site's very out of date resources. I'd like them to be able to throw in my custom CSS and edit it as they please so they can take it back to the site and implement it on their actual journals.

Note that I AM okay with having to change how this already functions if need be. I'd rather it work correctly than stick within what I already have. I'm not really looking to upload TinyMCE, but if options are slim then hey. Nothing for it, right?

$(function() {
  function change() {
  var iFrame =  document.getElementById('display-inner');
   var iFrameBody;
   if ( iFrame.contentDocument ) 
   { // FF
     iFrameBody = iFrame.contentDocument.getElementsByTagName('body')[0];
   }
   else if ( iFrame.contentWindow ) 
   { // IE
     iFrameBody = iFrame.contentWindow.document.getElementsByTagName('body')[0];
   }
    iFrameBody.innerHTML = document.getElementById('input-inner').value;
}
  });


$(function() {
 function saveTextAsFile()
        {
            var textToWrite = document.getElementById('input-inner').value;
            var textFileAsBlob = new Blob([textToWrite], {type:'text/plain'});
            var fileNameToSaveAs = "mycode_tessisamess.txt";
        
            var downloadLink = document.createElement("a");
            downloadLink.download = fileNameToSaveAs;
            downloadLink.innerHTML = "Download File";
            if (window.webkitURL != null)
            {
                // Chrome allows the link to be clicked
                // without actually adding it to the DOM.
                downloadLink.href = window.webkitURL.createObjectURL(textFileAsBlob);
            }
            else
            {
                // Firefox requires the link to be added to the DOM
                // before it can be clicked.
                downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
                downloadLink.onclick = destroyClickedElement;
                downloadLink.style.display = "none";
                document.body.appendChild(downloadLink);
            }
        
            downloadLink.click();
        }
    
        var button = document.getElementById('save');
        button.addEventListener('click', saveTextAsFile);
});
@import url(https://fonts.googleapis.com/css?family=Open+Sans);

::selection{background:#EAA2B9;}
::-moz-selection{background:#EAA2B9;}

body,html{height:100%;}
body{margin:0;}

#input, #display{display:inline-block;height:100%;vertical-align:top;overflow:hidden;}

#input{position:relative;background:#ddd url(http://i.imgur.com/wBSwx5F.jpg)center no-repeat fixed;width:35%;-webkit-box-shadow:inset -10px 0 10px -10px rgba(0,0,0,0.6);box-shadow:inset -10px 0 10px -10px rgba(0,0,0,0.6);}

#controls{font-family:Open Sans,Helvetica,arial,sans-serif;font-size:13px;text-align:right;height:24px;background:#fff;padding:7px;border-bottom:1px solid #ccc;-webkit-box-shadow:inset -10px 0 10px -10px rgba(0,0,0,0.6);box-shadow:inset -10px 0 10px -10px rgba(0,0,0,0.6);}
.c-button, select, option{background:#fff;cursor:pointer;border:1px solid rgba(0,0,0,0.3);border-radius:4px;padding:2px 10px;margin:0 2px;font-family:Open Sans,Helvetica,arial,sans-serif;}

#input-inner{position:absolute;bottom:4px;top:38px;width:97%;margin:0;background:transparent;border:none;padding:10px;color:#000;overflow:auto;}
input:focus, textarea:focus, select:focus, option:focus{outline:none;}

#display{width:65%;}
#display-inner{height:100%;overflow:auto;border:none;width:100%;}
#display-inner p{max-width:600px;display:block;margin:0 auto;padding:100px;text-align:justify;font-family:Open Sans,Helvetica,arial,sans-serif;line-height:150%;font-size:115%;color:#444;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>

<form id="input">
<div id="controls">Options:
<select ONCHANGE="document.getElementById('display-inner').src = this.options[this.selectedIndex].value">
<option value="http://tessisamess.com/freeedit.html">Free Edit</option>
<option value="http://tessisamess.com/completestyle.html">Complete Style</option>
<option value="http://tessisamess.com/generator.html">Generator</option>
</select>
<button type="button" value="save" id="save" class="c-button">save</button>
<input type="reset" class="c-button" value="clear">
</div>
<textarea id="input-inner" onkeyup="change();" placeholder="You are in Free Edit mode! You can paste your code here, or start coding from scratch.

To style a layout instead, select your base from the dropdown above, and start with the base CSS given to create a new layout, or replace it with the free layout coding you wish to edit.

All changes will appear on the righthand side as you make them."></textarea>
</form><!---


---><div id="display">
<iframe id="display-inner" src="http://tessisamess.com/freeedit.html">
</iframe>
</div>


</body>


Solution

  • This would be a very long answer if I were to cover all the aspects of what needs to happen for you to get it working the way you need it, so instead I'll give you some helpful hints.

    • You can get to the document of the iFrame from your main page with: document.getElementById('MY_IFRAME_ID').contentWindow.document
    • The Element.outerHTML and Element.innerHTML properties will be your best friend in retrieving and updating page code.
    • To display HTML, you can do: iframeDocument.getElementsByTagName('html')[0].outerHTML and that will contain the HTML for the entire page, including all the inline css/js. If you need the doctype also, read this. Insert all that into textarea.
    • To make updates you can grab the html from your textarea and insert it back with Element.innerHTML like so: iframeDocument.getElementsByTagName('html')[0].innerHTML = htmlFromTextarea
    • For the external css/js, you'll need a proper way to display the code, but I'll leave it to you to figure out the details of that.
    • To get the external css you just have to iterate over iframeDocument.styleSheets, and use a similar strategy as the one mentioned above, although there are some cross browser considerations you might want to keep in mind.
    • For external js, you can find them in a similar fashion. Iterate over iframeDocument.getElementsByTagName('script') and get their content with Element.innerHTML
    • You could implement strategies for updating just sections instead of the entire document each time, but that's a bit more involved. I recommend taking the "update entire document" route until you have a better understanding of the moving parts.
    • Syntax highlighting would be immensely helpful, so consider putting your code inside a code editor like "Ace".

    I hope this helps and good luck! Please don't forget to check this answer as correct if you felt that it addresses your question.