Author: | mdotz |
---|---|
Views Total: | 584 views |
Official Page: | Go to website |
Last Update: | January 19, 2018 |
License: | MIT |
Preview:

Description:
A minimal clean WYSIWYG HTML editor built using Document.designMode and Document.execCommand.
How to use it:
This Web Editor requires Font Awesome to provide the icons for the editor controls.
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
Create the controls for the editor.
<div class="bar" id="bar"> <div class="row"> <a href="#" class="far-left std" data-command='bold'><i class="fa fa-bold" aria-hidden="true"></i></a> <a href="#" class="std" data-command='italic'><i class="fa fa-italic" aria-hidden="true"></i></a> <a href="#" class="std" data-command='underline'><i class="fa fa-underline" aria-hidden="true"></i></a> <a href="#" class="std" data-command='justifyLeft'><i class="fa fa-align-left" aria-hidden="true"></i></a> <a href="#" class="std" data-command='justifyCenter'><i class="fa fa-align-center" aria-hidden="true"></i></a> <a href="#" class="std" data-command='justifyRight'><i class="fa fa-align-right" aria-hidden="true"></i></a> <a href="#" class="std" data-command='justifyFull'><i class="fa fa-align-justify" aria-hidden="true"></i></a> <a href="#" class="std" data-command='insertOrderedList'><i class="fa fa-list-ol" aria-hidden="true"></i></a> <a href="#" class="std" data-command='insertUnorderedList'><i class="fa fa-list-ul" aria-hidden="true"></i></a> <a href="#" class="std" class="far-right" data-command='src'><i>abc</i></a> </div> <div class="row"> <a href="#" class="std" data-command='undo'><i class="fa fa-undo" aria-hidden="true"></i></a> <a href="#" class="std" data-command='redo'><i class="fa fa-repeat" aria-hidden="true"></i></a> <a href="#" class="std" data-command='formatBlock'><i class="fa fa-header" aria-hidden="true"></i></a> <a href="#" class="std" data-command='indent'><i class="fa fa-indent" aria-hidden="true"></i></a> <a href="#" class="std" data-command='outdent'><i class="fa fa-outdent" aria-hidden="true"></i></a> <a href="#" class="std" data-command='subscript'><i class="fa fa-subscript" aria-hidden="true"></i></a> <a href="#" class="std" data-command='superscript'><i class="fa fa-superscript" aria-hidden="true"></i></a> <a href="#" id="font"><i class="fa fa-font" aria-hidden="true"></i></a> <a href="#" class="picker" id="fore"><i class="fa fa-font fore" aria-hidden="true"></i></a> <a href="#" class="picker" id="back"><i class="fa fa-font back" aria-hidden="true"></i></a> </div> <div id="color-row" class="hidden active"> <a href="#" class="ext"><i class="fa fa-circle" aria-hidden="true"></i></a> <a href="#" class="ext"><i class="fa fa-circle" aria-hidden="true"></i></a> <a href="#" class="ext"><i class="fa fa-circle" aria-hidden="true"></i></a> <a href="#" class="ext"><i class="fa fa-circle" aria-hidden="true"></i></a> <a href="#" class="ext"><i class="fa fa-circle" aria-hidden="true"></i></a> <a href="#" class="ext"><i class="fa fa-circle" aria-hidden="true"></i></a> <a href="#" class="ext"><i class="fa fa-circle" aria-hidden="true"></i></a> <a href="#" class="ext"><i class="fa fa-circle" aria-hidden="true"></i></a> <a href="#" class="ext"><i class="fa fa-circle" aria-hidden="true"></i></a> <a href="#" class="ext"><i class="fa fa-circle" aria-hidden="true"></i></a> </div> <div id="font-row" class="hidden active"> <a href="#" class="fill"></a> <a href="#" class="fill"></a> <a href="#" class="ext">1</a> <a href="#" class="ext">2</a> <a href="#" class="ext">3</a> <a href="#" class="ext">4</a> <a href="#" class="ext">5</a> <a href="#" class="ext">6</a> <a href="#" class="ext">7</a> <a href="#" class="fill"></a> </div> </div>
Create an iframe for the editable content.
<iframe id="editor" name="editor"> </iframe>
The CSS for the editor.
.bar { background-color: #9ca2b1; flex: 0.15; border-radius: 10px 10px 0 0; display: flex; flex-flow: column } .bar .row { flex: 1; display: flex; flex-flow: row; background-color: #b5c4ce; border-top-left-radius: 10px; border-top-right-radius: 10px } .bar .row.active a { background-color: #adb4c1 } .bar .row a { color: black; flex: 0.1; display: flex; align-items: center; justify-content: center; text-align: center; text-decoration: none; background-color: #b5c4ce } .bar .row a.fill { background-color: #b5c4ce } .bar .row a.fill:hover { background-color: #b5c4ce } .bar .row a:hover { background-color: #c7dfdd } .bar .row a.far-left { border-top-left-radius: 10px } .bar .row a.far-right { border-top-right-radius: 10px } .bar .row a.ext { font-size: 2vw } .bar .row a.active { background-color: #adb4c1; border-top-right-radius: 10px; border-top-left-radius: 10px } .bar .row a i { font-size: 1.6vw } .bar .row a i.fa.fa-font.fore { color: #ff6961; text-decoration: underline } .bar .row a i.fa.fa-font.back { background: #779ecb } #editor { background-color: #fff; flex: 0.85; padding: 1vw; border: none } .hidden { display: none }
The main script.
editor.document.designMode = 'On'; const colors = ['#ffffff' ,'#d3d3d3' ,'#000000','#c00000','#ff0000', '#ffc000','#ffff00','#92d050', '#00b050', '#00b0f0']; let foreColorState = false; let fontSizeAnchors = document.getElementById('font-row').getElementsByTagName('a'); let colorIcons = document.getElementById('color-row').getElementsByTagName('i'); let bar = document.getElementById('bar'); let fontRow = document.getElementById('font-row'); let colorRow = document.getElementById('color-row'); let edit = document.getElementById('editor'); let barStyle = getComputedStyle(bar); let editStyle = getComputedStyle(edit); let foreAnchor = document.getElementById('fore'); let backAnchor = document.getElementById('back'); let fontAnchor = document.getElementById('font'); let anchors = [foreAnchor, backAnchor, fontAnchor]; // For determining fore/back color bar option is enabled foreAnchor.addEventListener('click', function(e){ foreColorState = true; }); backAnchor.addEventListener('click', function(e){ foreColorState = false; });
The third bar handlers.
for(let i = 0; i < fontSizeAnchors.length; i++){ fontSizeAnchors[i].addEventListener('click', e => { editor.document.execCommand('fontSize', false, i+1); }); } for(let i = 0; i < colors.length; i++){ colorIcons[i].style.color = colors[i]; colorIcons[i].style.fontSize = '3vw'; colorIcons[i].parentNode.addEventListener('click', function(e){ let command = foreColorState ? 'foreColor' : 'hiliteColor'; editor.document.execCommand(command, false, colors[i]); }) }
The font anchor event handler.
fontAnchor.addEventListener('click', function(e){ if(colorRow.classList.contains('hidden')){ toggleBarFlex(); toggleRow(fontRow); } else{ hideRow(colorRow); toggleRow(fontRow); } if(fontRow.classList.contains('hidden')){ toggleActiveAnchor(null); } else { toggleActiveAnchor(fontAnchor); } });
The color anchor event handler.
let previousTarget; [].forEach.call(document.getElementsByClassName('picker'), v => { v.addEventListener('click', function(e) { if((previousTarget == null || previousTarget === e.currentTarget) || colorRow.classList.contains('hidden')){ if(fontRow.classList.contains('hidden')){ toggleBarFlex(); toggleRow(colorRow); } else { hideRow(fontRow); toggleRow(colorRow); } } if(colorRow.classList.contains('hidden')){ toggleActiveAnchor(null); } else{ if(foreColorState){ toggleActiveAnchor(foreAnchor); } else { toggleActiveAnchor(backAnchor); } } previousTarget = e.currentTarget; }); }); function toggleBarFlex(){ edit.style.flex = editStyle.flexGrow == 0.85 ? 0.775 : 0.85; bar.style.flex = barStyle.flexGrow == 0.15 ? 0.225 : 0.15; } function toggleRow(row){ row.classList.toggle('row'); row.classList.toggle('hidden'); } function hideRow(row){ row.classList.add('hidden'); row.classList.remove('row'); } function toggleActiveAnchor(activeAnchor){ anchors.forEach(a => { a.classList.remove('active'); }) if(activeAnchor != null){ activeAnchor.classList.add('active'); } }
The static anchor event handler.
[].forEach.call(document.getElementsByClassName('std'), v => { v.addEventListener('click', function(e){ //Using currentTarget instead of target due to propagation let command = e.currentTarget.getAttribute('data-command'); switch(command){ case 'src': let child = e.currentTarget.childNodes[0]; child.innerHTML = child.textContent === 'abc' ? '</>' : 'abc'; //TODO break; case 'formatBlock': editor.focus(); editor.document.execCommand(command, false, 'H1'); editor.focus(); break; default: editor.focus(); editor.document.execCommand(command, false, null); editor.focus(); } }) });