
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();
}
})
});






