Last active
June 25, 2023 16:22
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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><a></small></button> | |
<button title='Unlink' style='font-weight: normal; text-decoration: line-through' onmousedown="event.preventDefault();" onclick="command('unlink');"><small><a></small></button> | |
<span class='separator'></span> | |
<button title='List' style='font-weight: normal;' onmousedown="event.preventDefault();" onclick="command('insertUnorderedList');"><small><ul></small></button> | |
<button title='Ordered list' style='font-weight: normal;' onmousedown="event.preventDefault();" onclick="command('insertOrderedList');"><small><ol></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