Skip to content

Instantly share code, notes, and snippets.

@daformat
Last active June 25, 2023 16:22
Show Gist options
  • Save daformat/c285023ce9de18648ddff3be1828f802 to your computer and use it in GitHub Desktop.
Save daformat/c285023ce9de18648ddff3be1828f802 to your computer and use it in GitHub Desktop.
Add basic editing capability to any html document and web page, leveraging designMode to provide quick in-browser editor
/*
Thanks to Justin Fuller for his pen (https://codepen.io/Iamjfu/pen/oBYgWV)
*/
(() => {
const controls =
`<style>
#doc-design-mode-editor-controls {
user-select: none;
position: fixed;
background-color: #fffffff9;
text-align: center;
padding: 0.5em;
line-height: 1.8em;
font-size: 0.9em;
color: #777;
box-shadow: 1px -1px 36px rgba(0,0,0,0.1);
display: flex;
flex: 1 1 0;
flex-wrap: wrap;
justify-content: center;
align-items: center;
z-index: 1000;
cursor: default;
}
#doc-design-mode-editor-controls.left-side {
top: 50%;
transform: translateY(-50%);
left: 0;
width: 3.8em;
border: 1px solid #e3e3e3;
border-left: none;
}
#doc-design-mode-editor-controls.right-side {
top: 50%;
transform: translateY(-50%);
right: 0;
width: 3.8em;
border: 1px solid #e3e3e3;
border-right: none;
}
#doc-design-mode-editor-controls.bottom-side {
bottom: 0;
left: 0;
right: 0;
border-top: 1px solid #e3e3e3;
}
#doc-design-mode-editor-controls.top-side {
top: 0;
left: 0;
right: 0;
border-bottom: 1px solid #e3e3e3;
}
#doc-design-mode-editor-controls .separator {
width: 1em;
height: 1em;
display: inline-block;
}
#doc-design-mode-editor-controls.left-side .separator, #doc-design-mode-editor-controls.right-side .separator {
display: block;
}
#doc-design-mode-editor-controls > div {
flex-grow: 1;
margin: 0.2rem;
}
#doc-design-mode-editor-controls > div:last-of-type {
position: relative;
flex-grow: 0;
margin: 0.6rem;
}
#doc-design-mode-editor-controls > div:last-of-type > div {
width: 1.5rem;
height: 1.5rem;
}
#doc-design-mode-editor-controls > div:last-of-type button {
position: absolute;
border: 1px solid #777;
border-radius: 16px;
opacity: 0.25;
width: 8px;
height: 8px;
display: block;
padding: 0;
margin: 0;
font-size: 0;
}
#doc-design-mode-editor-controls > div:last-of-type button:hover,
#doc-design-mode-editor-controls.bottom-side > div:last-of-type button:nth-of-type(1),
#doc-design-mode-editor-controls.left-side > div:last-of-type button:nth-of-type(2),
#doc-design-mode-editor-controls.top-side > div:last-of-type button:nth-of-type(3),
#doc-design-mode-editor-controls.right-side > div:last-of-type button:nth-of-type(4) {
background-color: #777;
border-color: black;
opacity: 0.5;
box-shadow: 1px 1px 18px rgba(0,0,0,0.4);
}
#doc-design-mode-editor-controls > div:last-of-type button:nth-of-type(1) {
bottom: 0;
left: 50%;
transform: translateX(-50%);
z-index: 4;
}
#doc-design-mode-editor-controls > div:last-of-type button:nth-of-type(2) {
left: 0;
top: 50%;
transform: translateY(-50%);
z-index: 3;
}
#doc-design-mode-editor-controls > div:last-of-type button:nth-of-type(3) {
top: 0;
left: 50%;
transform: translateX(-50%);
z-index: 2;
}
#doc-design-mode-editor-controls > div:last-of-type button:nth-of-type(4) {
right: 0;
top: 50%;
transform: translateY(-50%);
z-index: 1;
}
#doc-design-mode-editor-controls button {
min-width: 2.75em;
margin: 0.1em;
border-radius: 2px;
line-height: inherit;
font-size: inherit;
color: inherit;
padding: 0.3em 0.7em;
border: 1px solid #e3e3e3;
appearance: none;
-webkit-appearance: none;
transition: all 0.2s ease-out
}
#doc-design-mode-editor-controls small {
font-size: 75%
}
#doc-design-mode-editor-controls button:hover {
border-color: #999;
color: #333;
cursor: pointer;
box-shadow: 1px 1px 18px rgba(0,0,0,0.01);
}
</style>
<div id='doc-design-mode-editor-controls' class='bottom-side' onmousedown="event.preventDefault();">
<div>
<button title='Bold' style=' font-weight: bold' onmousedown="event.preventDefault();" onclick="command('bold');">B</button>
<button title='Italic' style=' font-weight: bold; font-style: italic' onmousedown="event.preventDefault();" onclick="command('italic');">i</button>
<button title='Underline' style=' font-weight: bold; text-decoration: underline' onmousedown="event.preventDefault();" onclick="command('underline');">u</button>
<button title='Blockquote' style=' font-weight: bold;' onmousedown="event.preventDefault();" onclick="command('formatBlock', '<blockquote>');"><small>❞</small></button>
<button title='Superscript' style=' font-weight: bold;' onmousedown="event.preventDefault();" onclick="command('superscript');"><small>A<sup>a</sup></small></button>
<span class='separator'></span>
<button title='Link' style='font-weight: normal;' onmousedown="event.preventDefault();" onclick="url=prompt('Address to link to (url):'); command('createLink', url);"><small>&lt;a&gt;</small></button>
<button title='Unlink' style='font-weight: normal; text-decoration: line-through' onmousedown="event.preventDefault();" onclick="command('unlink');"><small>&lt;a&gt;</small></button>
<span class='separator'></span>
<button title='List' style='font-weight: normal;' onmousedown="event.preventDefault();" onclick="command('insertUnorderedList');"><small>&lt;ul&gt;</small></button>
<button title='Ordered list' style='font-weight: normal;' onmousedown="event.preventDefault();" onclick="command('insertOrderedList');"><small>&lt;ol&gt;</small></button>
<button title='Indent selection' style=' font-weight: bold;' onmousedown="event.preventDefault();" onclick="command('indent');"><small>⇥</small></button>
<button title='Outdent selection' style=' font-weight: bold;' onmousedown="event.preventDefault();" onclick="command('outdent');"><small>⇤</small></button>
<span class='separator'></span>
<button title='Blockquote' style=' font-weight: bold;' onmousedown="event.preventDefault();" onclick="command('formatBlock', '<h1>');"><small>h1</small></button>
<button title='Blockquote' style=' font-weight: bold;' onmousedown="event.preventDefault();" onclick="command('formatBlock', '<h2>');"><small>h2</small></button>
<button title='Blockquote' style=' font-weight: bold;' onmousedown="event.preventDefault();" onclick="command('formatBlock', '<h3>');"><small>h3</small></button>
<button title='Blockquote' style=' font-weight: bold;' onmousedown="event.preventDefault();" onclick="command('formatBlock', '<h4>');"><small>h4</small></button>
<!--
Not supported by chrome
-->
<!--
<button style=' font-weight: bold' onmousedown="event.preventDefault();" onclick="command('increaseFontSize');">A<small>A+</small></button>
<button style=' font-weight: bold' onmousedown="event.preventDefault();" onclick="command('decreaseFontSize');">A<small>A-</small></button>
-->
<span class='separator'></span>
<button title='Remove format' style=' font-weight: normal' onmousedown="event.preventDefault();" onclick="command('removeFormat');"><small>Abc</small></button>
<button title='Copy selection' style=' font-weight: normal;' onmousedown="event.preventDefault();" onclick="command('copy');"><small>Copy</small></button>
<button title='Cut selection' style=' font-weight: normal;' onmousedown="event.preventDefault();" onclick="command('cut');"><small>Cut</small></button>
</div>
<div>
<div>
<button onmousedown="event.preventDefault();" onclick="position('bottom');" title='Bottom'>Bottom</button>
<button onmousedown="event.preventDefault();" onclick="position('left');" title='Left'>Left</button>
<button onmousedown="event.preventDefault();" onclick="position('top');" title='Top'>Top</button>
<button onmousedown="event.preventDefault();" onclick="position('right');" title='Right'>Right</button>
</div>
</div>
</div>`;
document.designMode = 'on';
document.body.insertAdjacentHTML('beforeend', controls);
// This is the other alternative.
/*
const editor = document.querySelector('.editor');
editor.contentEditable = true;
editor.insertAdjacentHTML('beforeend', controls);
editor.focus();
*/
function command(name, arg) {
let success;
try {
success = document.execCommand(name, false, arg || null);
} catch (error) {
alert(error);
}
if (!success) {
const supported = isSupported(name);
const message = supported ? 'Unknown error. Is anything selected?' : 'Command is not supported by your browser.';
alert(message);
}
}
function position(pos) {
document.getElementById('doc-design-mode-editor-controls').className = `${pos}-side`;
}
function isSupported(name) {
return document.queryCommandSupported(name);
}
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment