Denna handledning visar hur du kan skapa en egen HTML-editor med ren JavaScript. Vi kommer att bygga en enkel HTML-editor som syftar till att användas av webbplatsadministratörer.
Den här enkla editorn (tillägget) fungerar med html-inmatning och har en knapp för att växla till visuell förhandsvisning. HTML-taggar placeras vid markörpositionen eller runt markerad text.
Det här tillägget har testats och fungerar med Google Chrome (75.0.3770.100), Mozilla Firefox (67.0.4), Microsoft Edge (42.17134.1.0) och Internet Explorer (11.829.17134.0), detta utan någon polyfill. Om du vill stödja äldre webbläsare kan du läsa vårt inlägg om transpilering och komplettering av JavaScript.
JavaScript
Den här klassen innehåller en konstruktor för att skapa en ny instans av tillägget, du kan ha många HTML-redigerare i samma formulär. Vi har lagt till händelsehanterare för alla knappar i tillägget och inkluderat offentliga metoder för att lägga till en tagg, för att skifta till förhandsgranskning, för att minifiera html och för att expandera html.
var annytab = annytab || {};
annytab.html = annytab.html || {};
annytab.html.editor = (function () {
'use_strict';
// Constructor
function editor(opts)
{
// Set default values for parameters
opts = opts || {};
// Set options
this.options = { button_container: 'htmlButtonPanel', textarea: 'txtContents', preview: 'txtPreview', button_class: 'annytab-form-button' };
for (var option in this.options)
{
if (opts.hasOwnProperty(option) === true)
{
this.options[option] = opts[option];
}
}
// Get references
this.container = document.getElementById(this.options.button_container);
this.ta = document.getElementById(this.options.textarea);
this.pw = document.getElementById(this.options.preview);
// Add buttons
var html = '<input type="button" class="' + this.options.button_class + '" style="font-weight:bold;" value="b" />';
html += '<input type="button" class="' + this.options.button_class + '" style="font-style:italic" value="i" />';
html += '<input type="button" class="' + this.options.button_class + '" value="br" />';
html += '<input type="button" class="' + this.options.button_class + '" value="h1" />';
html += '<input type="button" class="' + this.options.button_class + '" value="h2" />';
html += '<input type="button" class="' + this.options.button_class + '" value="h3" />';
html += '<input type="button" class="' + this.options.button_class + '" value="h4" />';
html += '<input type="button" class="' + this.options.button_class + '" value="p" />';
html += '<input type="button" class="' + this.options.button_class + '" value="code" />';
html += '<input type="button" class="' + this.options.button_class + '" value="span" />';
html += '<input type="button" class="' + this.options.button_class + '" value="div" />';
html += '<input type="button" class="' + this.options.button_class + '" value="line" />';
html += '<input type="button" class="' + this.options.button_class + '" value="space" />';
html += '<input type="button" class="' + this.options.button_class + '" value="url" />';
html += '<input type="button" class="' + this.options.button_class + '" value="img" />';
html += '<input type="button" class="' + this.options.button_class + '" value="font" />';
html += '<input type="button" class="' + this.options.button_class + '" value="list" />';
html += '<input type="button" class="' + this.options.button_class + '" value="table" /><br />';
html += '<div id="annytab-html-editor-preview" class="' + this.options.button_class + '">☊</div>';
html += '<div id="annytab-html-editor-minify" class="' + this.options.button_class + '">−</div>';
html += '<div id="annytab-html-editor-unminify" class="' + this.options.button_class + '">+</div>';
// Add html to the container (fastest way)
this.container.insertAdjacentHTML('beforeend', html);
// Add events
addEvents(this, this.container);
} // End of the constructor
// Toggle preview
editor.prototype.togglePreview = function (button) {
// Get buttons
var buttons = this.container.querySelectorAll('input');
// Check if the textarea is visible
if (isVisible(this.ta) === true) {
// Set preview window content
this.pw.innerHTML = this.ta.value;
// Set button text
button.innerHTML = '<>';
// Disable buttons
for (var i = 0; i < buttons.length; i++) {
buttons[i].setAttribute('disabled', true);
}
// Display preview window
this.ta.setAttribute('hidden', true);
this.pw.removeAttribute('hidden');
}
else
{
// Set button text
button.innerHTML = '☊';
// Enable buttons
for (i = 0; i < buttons.length; i++)
{
buttons[i].removeAttribute('disabled');
}
// Display textarea
this.ta.removeAttribute('hidden');
this.pw.setAttribute('hidden', true);
}
}; // End of the togglePreview method
// Minify html
editor.prototype.minify = function () {
// Remove tabs and line breaks
this.ta.value = this.ta.value.replace(/(?!<pre[^>]*?>)([\t](?!@))(?![^<]*?<\/pre>)/g, '');
this.ta.value = this.ta.value.replace(/(?!<pre[^>]*?>)([\r\n](?!@))(?![^<]*?<\/pre>)/g, '');
this.ta.value = this.ta.value.replace(/(?!<pre[^>]*?>)([\r](?!@))(?![^<]*?<\/pre>)/g, '');
this.ta.value = this.ta.value.replace(/(?!<pre[^>]*?>)([\n](?!@))(?![^<]*?<\/pre>)/g, '');
}; // End of the minify method
// Unminify html
editor.prototype.unminify = function () {
// Variables
var step = 0;
var content = '';
// Get all nodes
var nodes = this.ta.value.replace(/<\//g, '~~~</').replace(/</g, '~~~<').replace(/>/g, '>~~~').split('~~~');
// Loop all nodes
for (var i = 0; i < nodes.length; i++)
{
// Do not parse empty strings
if (nodes[i] === '') {
continue;
}
// Decrease step
if (nodes[i].search(/<\/[^/>]+>/g) !== -1) // End tag
{
step -= 1;
}
// Append to content
content += getTabs(step) + nodes[i] + '\n';
// Increase step
if (nodes[i].search(/<[^/>]+>/g) !== -1) // Start tag
{
step += 1;
}
}
// Set unminified content
this.ta.value = content;
}; // End of the unminify method
// Add a tag
editor.prototype.addTag = function (button)
{
// Insert the tag
if (button.value === "b") {
surroundSelectedText(this.ta, '<b>', '</b>');
}
else if (button.value === "i") {
surroundSelectedText(this.ta, '<i>', '</i>');
}
else if (button.value === "br") {
insertText(this.ta, '<br />');
}
else if (button.value === "h1") {
surroundSelectedText(this.ta, '<h1>', '</h1>');
}
else if (button.value === "h2") {
surroundSelectedText(this.ta, '<h2>', '</h2>');
}
else if (button.value === "h3") {
surroundSelectedText(this.ta, '<h3>', '</h3>');
}
else if (button.value === "h4") {
surroundSelectedText(this.ta, '<h4>', '</h4>');
}
else if (button.value === "p") {
surroundSelectedText(this.ta, '<p>', '</p>');
}
else if (button.value === "code") {
surroundSelectedText(this.ta, '<pre class="prettyprint annytab-code-container">', '</pre>');
}
else if (button.value === "span") {
surroundSelectedText(this.ta, '<span>', '</span>');
}
else if (button.value === "div") {
surroundSelectedText(this.ta, '<div>', '</div>');
}
else if (button.value === "line") {
surroundSelectedText(this.ta, '<div class="annytab-basic-line">', '</div>');
}
else if (button.value === "space") {
surroundSelectedText(this.ta, '<div class="annytab-basic-space">', '</div>');
}
else if (button.value === "url") {
surroundSelectedText(this.ta, '<a href="http://www.annytab.se" rel="nofollow" target="_blank">', '</a>');
}
else if (button.value === "img") {
surroundSelectedText(this.ta, '<img src="/source.jpg" style="max-width:100%;" />', '');
}
else if (button.value === "font") {
surroundSelectedText(this.ta, '<span style="font-family:Arial;color:#ff0000;">', '</span>');
}
else if (button.value === "list") {
insertText(this.ta, '<ul><li>r1</li><li>r2</li></ul>');
}
else if (button.value === "table") {
insertText(this.ta, '<table style="width:400px;text-align:center;"><tr><th>r1:c1</th><th>r1:c2</th></tr><tr><td>r2:c1</td><td>r2:c2</td></tr></table>');
}
}; // End of the addTag method
// Add events
function addEvents(ec, container)
{
// Get buttons
var buttons = container.querySelectorAll('input');
var pwb = container.querySelector('#annytab-html-editor-preview');
var umb = container.querySelector('#annytab-html-editor-unminify');
var mb = container.querySelector('#annytab-html-editor-minify');
// Add toggle preview click event
window.onload = pwb.addEventListener('click', function (event) {
// Prevent default click behavior
event.preventDefault();
// Preview
ec.togglePreview(this);
}, false);
// Add minify click event
window.onload = mb.addEventListener('click', function (event) {
// Prevent default click behavior
event.preventDefault();
// Minify html
ec.minify();
}, false);
// Add unminify click event
window.onload = umb.addEventListener('click', function (event) {
// Prevent default click behavior
event.preventDefault();
// Unminify html
ec.unminify();
}, false);
// Loop buttons
for (var i = 0; i < buttons.length; i++)
{
// Add a click event
window.onload = buttons[i].addEventListener('click', function (event) {
// Prevent default click behavior
event.preventDefault();
// Add a tag
ec.addTag(this);
}, false);
}
} // End of the addEvents method
// Get a selection
function getSelection(el)
{
var start = el.selectionStart;
var end = el.selectionEnd;
return {
start: start,
end: end,
length: end - start,
text: el.value.slice(start, end)
};
} // End of the getSelection method
// Surround selected text
function surroundSelectedText(el, before, after)
{
// Get selection
var selection = getSelection(el);
// Replace text
el.value = el.value.slice(0, selection.start) + before + selection.text + after + el.value.slice(selection.end);
// Set cursor
el.selectionEnd = selection.end + before.length + after.length;
el.blur();
el.focus();
} // End of the surrondSelectedText method
// Insert text at selection
function insertText(el, text) {
// Get selection
var selection = getSelection(el);
// Insert text
el.value = el.value.slice(0, selection.start) + text + el.value.slice(selection.end);
// Set cursor
el.selectionEnd = selection.end + text.length;
el.blur();
el.focus();
} // End of the insertText method
// Check if an element is visible
function isVisible(el)
{
return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
} // End of the isVisible method
// Get tabs
function getTabs(step) {
var tabs = '';
for (var i = 0; i < step; i++) {
tabs += '\t';
}
return tabs;
} // End of the getTabs method
// Return this object
return editor;
})();
Så här använder du editorn
Du måste skapa en behållare för knappar, en textarea och en förhandsgranskningsbehållare för varje redigerare som du vill lägga till i ditt formulär. Varje behållare behöver ett id, dessa identiteter läggs till som alternativ i konstruktorn.
<div class="annytab-form-label">Textarea</div>
<div id="htmlButtonPanel" style="border: 1px solid #808080;border-bottom: 0;background-color: #e9e9e9;padding: 0 10px 10px 10px;"></div>
<textarea id="txtContents" name="txtContents" style="width:100%;height:400px;padding:10px;border: 1px solid #808080;font-family:Arial;font-size:16px;line-height:24px;"></textarea>
<div id="txtPreview" style="width:100%;height:400px;padding:10px;border: 1px solid #808080;font-size:16px;line-height:24px;overflow:auto;" hidden></div>
<div class="annytab-form-label">Textarea</div>
<div id="htmlButtonPanel2" style="border: 1px solid #808080;border-bottom: 0;background-color: #e9e9e9;padding: 0 10px 10px 10px;"></div>
<textarea id="txtContents2" name="txtContents" style="width:100%;height:400px;padding:10px;border: 1px solid #808080;font-family:Arial;font-size:16px;line-height:24px;"></textarea>
<div id="txtPreview2" style="width:100%;height:400px;padding:10px;border: 1px solid #808080;font-size:16px;line-height:24px;overflow:auto;" hidden></div>
Skapa nya editors i en skript-tagg och lägg till alternativ i konstruktorn, standardvärden kommer att användas om alternativ inte läggs till. Du kanske vill minifiera html innan du skickar ditt formulär, du kan anropa den offentliga metoden minify via editorn för att göra detta.
<script src="/js/annytab-shared/annytab.html.editor.js"></script>
<script>
var options = { button_container: 'htmlButtonPanel', textarea: 'txtContents', preview: 'txtPreview', button_class: 'annytab-form-button' };
var panel = new annytab.html.editor(options);
var panel2 = new annytab.html.editor({ button_container: 'htmlButtonPanel2', textarea: 'txtContents2', preview: 'txtPreview2', button_class: 'annytab-form-button' });
// Public methods
panel.minify();
panel.unminify();
</script>