ixhtml
v2.6.1
Published
Ein leichtgewichtiger WYSIWYG-HTML-Editor ohne Laufzeitabhaengigkeiten.
Maintainers
Readme
ixHTML
ixHTML ist ein leichtgewichtiger WYSIWYG-HTML-Editor in reinem JavaScript. Er hat keine JavaScript-Laufzeitabhaengigkeiten. Die Bootstrap Icons werden beim Erzeugen des ersten Editors ueber ein CDN geladen.
Links werden ueber einen eigenen modalen Dialog eingefuegt und bearbeitet. Dabei lassen sich URL, Anzeigetext, Titel und das Zielfenster festlegen.
Auch der HTML-Quellcode wird in einem eigenen Modal angezeigt. Dort kann er bearbeitet oder ersetzt und anschliessend in den Editor uebernommen werden.
Iframes im Quelltext
Iframes sind standardmaessig deaktiviert und werden beim Einlesen, Bearbeiten und Ausgeben entfernt. Mit allowIframe: true koennen sie ueber die Quelltext-Funktion eingefuegt werden:
const editor = new ixHTML(element, {
allowIframe: true
});Im Editor selbst erscheint an der Position des Iframes nur ein nicht editierbarer Platzhalter mit dessen src-URL. getHtml() und die Quelltext-Funktion liefern weiterhin das vollstaendige <iframe> zurueck. Attribute wie style, width, height, allow, sandbox und loading bleiben dabei erhalten.
Layout-Regeln, die Inhalte aus dem normalen Editorfluss loesen oder die gesamte Vorschau ueberdecken koennen, werden nur innerhalb der Vorschau neutralisiert. Dazu gehoeren unter anderem position: absolute, position: fixed, Offsets wie top und left, hohe z-index-Werte, Transformationen sowie Groessen mit Viewport-Einheiten wie 100vw oder 100vh. Im ausgegebenen HTML und im Quelltext bleiben diese Regeln vollstaendig erhalten.
Wird ein Iframe bei deaktivierter Option entfernt, blendet der Editor kurz einen Info-Hinweis ein. Dieselbe Notification-Komponente kann auch fuer eigene Meldungen verwendet werden:
editor.showNotification("Inhalt wurde gespeichert.", {
type: "success", // info, success, warning oder error
duration: 3200
});Tabellen lassen sich ueber ein 10 × 8-Auswahlgitter in der Toolbar einfuegen. Ein Ziehen von einer Zelle zu einer anderen markiert immer den vollstaendigen rechteckigen Zellbereich – auch vertikal und diagonal. Eine schwebende Tabellen-Toolbar bietet Aktionen zum Verbinden und Teilen sowie zum Einfuegen und Entfernen von Zeilen und Spalten. Die vier Eckgriffe einer aktiven Tabelle aendern ihre Breite und Hoehe per Drag-and-drop.
Listenpunkte lassen sich mit Tab eine Ebene einruecken und mit Shift+Tab wieder ausruecken, auch bei mehreren markierten Punkten. Nummerierte Listen wechseln je Ebene zwischen 1, 2, 3, a, b, c und i, ii, iii; Aufzaehlungen zwischen Punkt, Kreis und Quadrat. Die Listenzeichen bleiben im ausgegebenen HTML erhalten. Ein- und Ausruecken unterstuetzen Undo/Redo. Ausserhalb von Listen behaelt Tab seine normale Funktion.
Neu in 2.6.1
- Paketversion und Versionsauskunft ueber
ixHTML.getVersion()auf2.6.1aktualisiert.
Neu in 2.6.0
- Listenpunkte lassen sich mit
Tabeinruecken und mitShift+Tabausruecken, auch bei mehreren markierten Punkten. - Nummerierte Listen wechseln je Ebene zwischen Zahlen, Buchstaben und roemischen Zahlen; Aufzaehlungen zwischen Punkt, Kreis und Quadrat.
- Die Listenzeichen bleiben im ausgegebenen HTML erhalten. Ein- und Ausruecken unterstuetzen Undo/Redo.
- Ausserhalb von Listen behaelt
Tabseine normale Funktion.
Neu in 2.5.0
- Neues
allowIframe-Flag: Iframes sind standardmaessig deaktiviert und werden konsequent entfernt. - Erlaubte Iframes bleiben inklusive ihrer Attribute und Inline-Styles im HTML-Output erhalten.
- Im Editor werden Iframes niemals geladen, sondern als sichere Platzhalter mit ihrer URL angezeigt.
- Riskante Layout-Styles wie feste oder absolute Positionierung, Viewport-Groessen und hohe
z-index-Werte werden nur in der Vorschau neutralisiert und im Output wiederhergestellt. - Eine wiederverwendbare Notification-Komponente informiert ueber entfernte Iframes und steht ueber
showNotification()auch fuer eigene Meldungen zur Verfuegung.
Neu in 2.4.1
- Der Colorpicker wird als viewportgebundenes Overlay ausserhalb des Editor-Containers gerendert und kann dadurch ueber dessen Raender hinausragen, auch wenn ein umgebendes Element Inhalte abschneidet.
- Beim Scrollen und bei Groessenaenderungen bleibt der Colorpicker am Toolbar-Button ausgerichtet. Wenn unterhalb nicht genug Platz vorhanden ist, wird er automatisch oberhalb angezeigt.
- Theme, CSS-Variablen und individuelle
popover-Regeln gelten weiterhin fuer den ausgelagerten Colorpicker.
Neu in 2.4.0
- Der native Textfarben-Dialog wurde durch einen eigenen Colorpicker direkt am Toolbar-Button ersetzt. Aenderungen ueber Farbfläche, Farbton-Regler oder Hex-Eingabe werden ohne zusaetzlichen Apply-Button sofort angewendet.
- Die aktuelle Textfarbe an Cursor oder Auswahl wird im Toolbar-Button und Colorpicker angezeigt.
fontColorListersetzt die vorgeschlagenen Farben durch eine kommaseparierte Liste von Hexcodes. Ein leerer String blendet die Farbliste aus.- Mit
fontColorPicking: falsewerden die freie Farbauswahl und Hex-Eingabe ausgeblendet, sodass nur die Farben ausfontColorListzur Verfuegung stehen. - Die Standardpalette enthaelt Schwarz, Rot, Gruen, Blau und Gelb. Bis zu fuenf Farben fuellen eine Zeile gleichmaessig aus; groessere Listen umbrechen und sind ab 300 px Hoehe scrollbar.
Neu in 2.3.10
- Wird der Editor mit einem
licenseKeyinitialisiert, zeigt die Freischaltungsansicht waehrend der Lizenzpruefung einen Loader anstelle des Eingabefelds. - Ist der initial mitgegebene Lizenzschluessel nicht mehr gueltig, erscheint eine lokalisierte Fehlermeldung; das Eingabefeld bleibt verborgen.
- Ohne initialen
licenseKeysteht das manuelle Eingabefeld weiterhin wie gewohnt zur Verfuegung.
Neu in 2.3.9
<b>und<strong>erhalten innerhalb des Editors standardmaessigfont-weight: 700, damit globale CSS-Resets der Host-Anwendung die Fettformatierung und deren Toolbar-Status nicht aufheben.
Neu in 2.3.8
- Die geladene Paketversion kann fuer Entwicklerdiagnosen ueber
ixHTML.getVersion()abgefragt werden, ohne in der Editor-Oberflaeche angezeigt zu werden.
Neu in 2.3.7
- Bei mehreren Editor-Instanzen werden aktive Formatierungen wie Fett und Kursiv nur noch in der Toolbar der Instanz hervorgehoben, zu der die aktuelle Auswahl gehoert.
- Die Schriftart-Auswahl uebernimmt keinen Zustand mehr aus einer anderen Editor-Instanz.
Neu in 2.3.6
- Die visuelle Markierung eines aktiven Links wird jetzt ohne Rahmen, abgerundete Ecken oder Rand-Schatten dargestellt.
- Die dezente Hintergrundfarbe bleibt erhalten und kennzeichnet weiterhin den gesamten sichtbaren Linkbereich.
Neu in 2.3.5
- Ein normaler Klick innerhalb eines
<a>-Links markiert dessen gesamten sichtbaren Bereich, ohne die Cursorposition oder das gespeicherte HTML zu veraendern. - Mehrzeilige Links erhalten fuer jede sichtbare Textzeile eine passende Markierung.
- Der aktive Link kann ueber den dadurch freigeschalteten
Link entfernen-Button direkt geloest werden. - Die neue CSS-Komponente
linkSelectionerlaubt individuelles Styling der Link-Markierung.
Neu in 2.3.4
- Formatierungen aus Toolbar und Tastatur erhalten eigene History-Schritte und lassen sich einzeln rueckgaengig machen oder wiederholen.
- Listen- und weitere ueber
beforeinputgemeldete Formatierungen werden ebenfalls in die History aufgenommen. - Noch nicht abgeschlossene Texteingaben werden beim ersten Undo als Redo-Ziel gesichert, sodass ein Undo unmittelbar genau einen Schreibschritt zurueckgeht.
- Redo steht direkt nach diesem ersten Undo zur Verfuegung und stellt den zuvor offenen Textzustand wieder her.
Neu in 2.3.3
- Der Link-Button zeigt durch seinen aktiven Zustand an, wenn Cursor oder Auswahl in einem normalen
<a>-Link liegen. Link entfernenist ausserhalb eines Links deaktiviert und wird nur bei einer Link-Auswahl freigeschaltet.- Ein deutlicheres, durchgestrichenes Link-Symbol macht die Entfernen-Aktion besser erkennbar.
- Toolbar-Aktionen erhalten die aktuelle Textauswahl, damit Linkbefehle auf der erwarteten Auswahl ausgefuehrt werden.
Neu in 2.3.2
- Plugin-Buttons koennen registrierte Uebersetzungen deklarativ ueber
titleKeyverwenden. setLocale()aktualisiert Tooltip und ARIA-Beschriftung solcher Plugin-Buttons sofort, ohne das Plugin oder den Editor neu aufzubauen.- Das Internal-Link-Plugin bringt Uebersetzungen fuer Deutsch, Englisch, Niederlaendisch, Spanisch, Franzoesisch, Italienisch und Daenisch mit.
Neu in 2.3.1
- Bilder lassen sich per Klick auswaehlen und ueber eine schwebende Aktion sowie
EntfoderBackspaceentfernen. - Ausgewaehlte Bilder koennen per Drag-and-drop innerhalb des Editors verschoben werden. Eine Live-Vorschau zeigt bereits beim Ziehen die neue Bildposition und den angepassten Textfluss.
Escoder Ablegen ausserhalb des Editors stellt die urspruengliche Position wieder her. - Vier Eckgriffe skalieren Bilder proportional; alle Aenderungen werden von Undo und Redo erfasst.
- Links, die ausschliesslich ein Bild umschliessen, werden beim Verschieben und Loeschen zusammen mit dem Bild behandelt.
- Neue CSS-Komponenten
imageSelection,imageToolbarundimageResizeHandlefuer individuelles Styling.
Neu in 2.3.0
- Medienfaehige Plugin-API mit konfigurierbarem
filePickerundapi.pickFile(). - Zentrale URL-Anpassung ueber
urlConverterundapi.convertUrl(). api.sanitizeHtml()undapi.encodeHtml()fuer Inhalte aus Benutzereingaben und externen Quellen.- Eigene Plugin-Commands mit
api.registerCommand()sowie Menueintraege mitapi.addMenuItem(). - Plugin-Modals unterstuetzen Tabs,
filepicker-Felder und Feldereignisse.
Neu in 2.2.0
- Oeffentliche Selection-API fuer Plugins mit speicher- und wiederherstellbaren
Range-Snapshots. - Neues
selectionChange-Event fuer Cursor- und Auswahlwechsel im Editor. - Plugin-Toolbar-Buttons koennen mit
setActive()undsetDisabled()gesteuert werden. api.transaction()fasst direkte DOM-Aenderungen samt Undo/Redo und Toolbar-Aktualisierung zusammen.- Plugin-Buttons unterstuetzen neben Bootstrap-Icon-Klassen jetzt auch SVG-Markup, DOM-Nodes und Icon-Factorys.
Neu in 2.1.0
- Direkte Editor-Events ueber
editor.on(eventName, listener)mit Unsubscribe-Funktion. - Umschaltbarer Dark Mode ueber
darkMode,setDarkMode()undgetDarkMode(). - Eigene ueberschreibbare
--ixhtml-dark-*-Variablen fuer die komplette dunkle Palette. - Im Dark Mode umgekehrte, helle Tooltips mit separat konfigurierbarem Hintergrund und Text.
CSS-API und individuelles Design
Der Editor stellt zentrale Designwerte als CSS Custom Properties bereit. Sie koennen direkt am Host gesetzt werden und gelten dadurch nur fuer die jeweilige Instanz:
#editor {
--ixhtml-primary: #7c3aed;
--ixhtml-background: #fcfcff;
--ixhtml-surface-muted: #f4f1ff;
--ixhtml-text-color: #211a2f;
--ixhtml-border-color: #d8d0e8;
--ixhtml-font-family: Inter, sans-serif;
--ixhtml-font-size: 15px;
--ixhtml-editor-padding: 24px;
--ixhtml-toolbar-padding: 10px;
--ixhtml-table-cell-padding: 12px;
}Verfuegbar sind ausserdem --ixhtml-muted-color, --ixhtml-surface, --ixhtml-hover-background, --ixhtml-active-background, --ixhtml-control-border-color, --ixhtml-table-border-color, --ixhtml-table-header-background, --ixhtml-selection-background, --ixhtml-border-radius und --ixhtml-line-height.
Dieselben Werte lassen sich bei der Initialisierung oder spaeter per JavaScript setzen. Der Prefix --ixhtml- ist bei den API-Methoden optional:
const editor = new ixHTML(document.getElementById("editor"), {
css: {
variables: {
primary: "#7c3aed",
borderRadius: "8px"
},
rules: {
editor: { maxWidth: "760px", margin: "0 auto" },
tableCell: { minWidth: "80px" },
buttonHover: { transform: "translateY(-1px)" }
}
}
});
editor.setCssVariable("primary", "#2563eb");
editor.setCssVariables({ fontSize: "17px", editorPadding: "20px" });
editor.setCssRules({ toolbar: { borderBottomWidth: "2px" } });
console.log(editor.getCssVariable("primary"));
console.log(editor.getCssRules());
editor.resetCss();Bilder im Editor lassen sich per Klick auswählen. Die Auswahl bietet einen Löschbutton, reagiert auf Entf und Backspace, kann per Drag-and-drop verschoben und über die vier Eckgriffe proportional skaliert werden. Umschließt ein Link ausschließlich das Bild, wird dieser beim Verschieben oder Löschen als Einheit behandelt.
Die Bild-Toolbar bietet außerdem Icon-Buttons für ALT-Text, Bildunterschrift und Copyright mit übersetzten Tooltips. Sie öffnen jeweils einen Eingabedialog. ALT-Text wird direkt im alt-Attribut des Bildes gespeichert; Copyright erhält automatisch das ©-Symbol. Die Texte erscheinen unter dem Bild und bleiben beim Verschieben, Löschen sowie Undo/Redo mit ihm verbunden. Erneutes Öffnen bearbeitet den vorhandenen Text; ein leeres Feld entfernt ihn. Die Zuordnung bleibt auch im exportierten HTML erhalten. Links-, Mittel- und Rechtsausrichtung behandeln Bild und Texte als gemeinsame Einheit.
Regeln sind absichtlich an stabile Komponentennamen gebunden und werden automatisch auf die aktuelle Editor-Instanz begrenzt. Unterstuetzt werden root, toolbar, editor, footer, button, buttonHover, buttonActive, select, popover, pluginMenu, pluginMenuItem, table, tableCell, tableHeader, tableToolbar, tableSelection, resizeHandle, imageSelection, imageToolbar, imageResizeHandle, linkSelection, notificationContainer, notification, source, tooltip, licenseGate, licenseCard, modalBackdrop, modal, modalHeader, modalTabs, modalTab, modalBody, modalActions, input und action. Auch ausserhalb des Editor-Containers gerenderte Dialoge werden dabei korrekt ihrer Instanz zugeordnet. CSS-Eigenschaften koennen in camelCase oder kebab-case angegeben werden.
Datei- und URL-Callbacks fuer Plugins
Anwendungen koennen ihren eigenen Asset-Manager an den Editor anbinden. Der Callback darf direkt ein Ergebnis oder ein Promise liefern:
const editor = new ixHTML(element, {
licenseKey: "IXH1-...",
filePicker: async function (request) {
const asset = await openAssetManager({
type: request.type,
accept: request.accept,
plugin: request.plugin
});
return asset ? {
url: asset.url,
name: asset.name,
type: asset.mimeType,
meta: asset.meta
} : null;
},
urlConverter: function (url, context) {
return context.type === "poster" ? cdn.imageUrl(url) : cdn.mediaUrl(url);
}
});Ohne eigenen filePicker oeffnet api.pickFile() die native Dateiauswahl des Browsers und liefert fuer die ausgewaehlte Datei eine lokale Blob-URL. Solche Blob-URLs sind nur fuer die aktuelle Browser-Sitzung geeignet; dauerhaft gespeicherte Inhalte sollten eine URL aus einem Upload- oder Asset-System verwenden.
ixHTML.getCssComponents() liefert die verfuegbaren Komponentennamen, ixHTML.getCssVariableDefaults() alle Designvariablen samt Standardwerten.
Dark Mode
Der Dark Mode kann bei der Initialisierung aktiviert und danach jederzeit umgeschaltet werden:
const editor = new ixHTML(element, {
darkMode: true,
css: {
variables: {
darkPrimary: "#b5a7ff",
darkBackground: "#0b1210",
darkSurface: "#121c18"
}
}
});
editor.setDarkMode(false);
editor.setDarkMode(true);
console.log(editor.getDarkMode());Die Dark-Mode-Palette verwendet eigene ueberschreibbare Variablen: --ixhtml-dark-primary, --ixhtml-dark-background, --ixhtml-dark-surface, --ixhtml-dark-surface-muted, --ixhtml-dark-text-color, --ixhtml-dark-muted-color, --ixhtml-dark-hover-background, --ixhtml-dark-active-background, --ixhtml-dark-border-color, --ixhtml-dark-control-border-color, --ixhtml-dark-table-border-color, --ixhtml-dark-table-header-background, --ixhtml-dark-selection-background, --ixhtml-dark-tooltip-background und --ixhtml-dark-tooltip-text-color. Bei den JavaScript-Methoden kann der Prefix entfallen, beispielsweise editor.setCssVariable("darkSurface", "#171f1b"). Tooltips werden im Dark Mode standardmaessig hell mit dunkler Schrift dargestellt.
Als lokales Package verwenden
Das Verzeichnis kann direkt in die package.json eines anderen Projekts eingetragen werden:
{
"dependencies": {
"ixhtml": "file:../WYSIWYG"
}
}Danach die Abhaengigkeiten mit npm install installieren und den Editor verwenden:
const ixHTML = require("ixhtml");
const editor = new ixHTML(document.getElementById("editor"), {
licenseKey: "IXH1-...",
placeholder: "Schreibe etwas...",
initialHtml: "<p>Hallo!</p>"
});
editor.ready.then(function () {
console.log(editor.getHtml());
});Mit einem Bundler ist auch ein ES-Import moeglich:
import ixHTML from "ixhtml";Der Ziel-Container benoetigt eine Hoehe, damit der Editor sichtbar ist:
#editor {
width: 100%;
height: 500px;
}Lizenzierung
ixHTML 2.x benoetigt immer eine gueltige Lizenz vom fest konfigurierten Lizenzserver https://ixhtml.konzept-ix.com.
const editor = new ixHTML(element, {
licenseKey: "IXH1-12345-ABCDE-..."
});
await editor.ready;Ohne licenseKey zeigt der Editor eine lokalisierte Freischaltungsansicht mit Eingabefeld. Wird beim Initialisieren ein licenseKey mitgegeben, zeigt die Freischaltungsansicht während der Prüfung stattdessen einen Loader und bei einer fehlgeschlagenen Prüfung eine Fehlermeldung; das Eingabefeld wird in diesem Modus nicht angezeigt. Vor erfolgreicher Aktivierung sind Inhaltsbereich, Editor-API und Plugins gesperrt.
Der rohe Lizenz-Key wird nicht gespeichert. Nach der Aktivierung wird ausschliesslich das vom Server Ed25519-signierte Token gespeichert. Es ist an die aktuelle Origin und eine zufaellige lokale Installations-ID gebunden. Vorhandene gueltige Tokens werden beim naechsten Laden lokal geprueft und wiederverwendet. Der oeffentliche Pruefschluessel wird aus Sicherheitsgruenden pro Browser-Sitzung erneut ueber HTTPS geladen und nicht dauerhaft als vertrauenswuerdiger Schluessel im Local Storage abgelegt.
editor.getLicenseStatus();
// { status: "active", expiresAt: "...", features: ["editor", "plugins"] }
const success = await editor.activateLicense("IXH1-...");
await editor.deactivateLicense();Moegliche Zustaende sind locked, checking, active und invalid. Die Lizenzpruefung benoetigt HTTPS; localhost ist fuer lokale Entwicklung ebenfalls geeignet. Direkt ueber file:// geoeffnete Seiten koennen nicht aktiviert werden.
Sprache und Lokalisierung
Die Sprache kann beim Erzeugen des Editors mit locale festgelegt werden:
const editor = new ixHTML(document.getElementById("editor"), {
locale: "en-US"
});Regionscodes wie de-DE, en-US, fr-CA oder pt-BR werden automatisch auf eine vorhandene Basissprache aufgeloest. Mitgeliefert werden:
de– Deutsch, zugleich Standarden– Englischfr– Franzoesisches– Spanischit– Italienischpt– Portugiesisch
Die Sprache kann jederzeit ohne Neuaufbau und ohne Inhaltsverlust gewechselt werden:
editor.setLocale("fr");
console.log(editor.getLocale()); // "fr"Dabei werden Toolbar, Tooltips, Platzhalter, Tabellensteuerung, Link-, Quellcode- und Suchen/Ersetzen-Modals sowie ARIA-Beschriftungen aktualisiert. Ein explizit gesetzter eigener placeholder bleibt bei Sprachwechseln unveraendert.
Alle vorhandenen Sprachen sind abfragbar:
ixHTML.getAvailableLocales(); // ["de", "en", "fr", "es", "it", "pt"]Weitere Sprachen koennen vor der Editorinitialisierung registriert werden. Fehlende Schluessel fallen automatisch auf Englisch zurueck:
ixHTML.registerLocale("nl", {
placeholder: "Schrijf iets...",
undo: "Ongedaan maken",
redo: "Opnieuw"
});
const editor = new ixHTML(element, { locale: "nl-NL" });Plugins erhalten mit api.getLocale() den aktuellen Sprachcode und mit api.t(key, values) Zugriff auf die Editor-Uebersetzungen. Deklarative Plugin-Buttons koennen einen registrierten Schluessel ueber titleKey direkt binden:
ixHTML.registerLocale("de", { internalLinkTitle: "Internen Link bearbeiten" });
ixHTML.registerLocale("en", { internalLinkTitle: "Edit internal link" });
api.addToolbarButton({
titleKey: "internalLinkTitle",
icon: "bi-link",
onClick: openInternalLinkDialog
});Tooltip und ARIA-Beschriftung solcher Buttons werden bei editor.setLocale() automatisch aktualisiert. Fuer frei erzeugte Plugin-Elemente kann weiterhin auf das Laufzeitereignis reagiert werden:
api.on("localeChange", function (event) {
console.log(event.locale);
cancelButton.textContent = api.t("cancel");
});Plugin-eigene Texte muessen weiterhin vom Plugin registriert werden. Automatisch gebundene titleKey-Texte aktualisiert der Editor selbst; bei frei erzeugten Elementen uebernimmt das Plugin die Aktualisierung im localeChange-Listener. Das WordCount-Beispiel in index.html zeigt die zweite Variante.
Direkt im Browser verwenden
<div id="editor" style="height: 500px"></div>
<script src="./node_modules/ixhtml/ixhtml.js"></script>
<script>
const editor = new ixHTML(document.getElementById("editor"), {
licenseKey: "IXH1-..."
});
editor.ready.then(function () {
console.log("Editor freigeschaltet");
});
</script>Links konfigurieren
createLink akzeptiert neben true/false ein Konfigurationsobjekt:
ixHTML.registerLocale("de", { inApp: "In App", inBrowser: "Im Browser" });
const editor = new ixHTML(element, {
licenseKey: "IXH1-...",
createLink: {
showOpenIn: true,
openIn: [
{ title: "In App", titleKey: "inApp", target: "_self" },
{ title: "In Browser", titleKey: "inBrowser", target: "_blank" }
],
defaultTextColor: "blue",
underline: true,
arrow: false
}
});showOpenIn: Zeigt die Zielauswahl „Öffnen in“ (Standard:true). Ausgeblendet verwendet ein neuer Link das erste Ziel; vorhandene Links behalten ihr Ziel.openIn: Eigene Ziele mittarget, optionalemtitleundtitleKey. Übersetzungen werden überixHTML.registerLocaleregistriert und beim Sprachwechsel aktualisiert. Ohne Übersetzung dienttitleals Ersatz. Eine leere oder fehlende Liste verwendet_self,_blank,_parentund_top.defaultTextColor: Initiale Linkfarbe als CSS-Farbwert, etwa"blue","#0000ff"oder"0000ff"(Standard:"blue"). Hexfarben funktionieren mit und ohne#, auch in Kurzform ("#00f"/"00f").underline: Initiale Unterstreichung (Standard:true).arrow: Kleiner Teilen-Pfeil↗am Linkende (Standard:false).
Farbe, Unterstreichung und Pfeil stehen im Link-Modal nebeneinander. Die Farbe verwendet den Custom-Farbpicker mit den globalen Optionen fontColorList und fontColorPicking. Optional können beide Optionen innerhalb von createLink für Linkfarben separat gesetzt werden, etwa fontColorList: "0000ff,663399,ff0000" und fontColorPicking: false für eine reine Palette. Die Auswahl wird erst beim Speichern des Links angewendet; Abbrechen verwirft sie. Beim Bearbeiten werden die vorhandenen Werte geladen. Das exportierte HTML enthält die Darstellung einschließlich Pfeil; beim Entfernen des Links wird auch der Pfeil entfernt. Die Option underline auf oberster Ebene steuert weiterhin den Toolbar-Button.
Toolbar konfigurieren
Alle integrierten Toolbar-Funktionen sind standardmaessig aktiviert. Einzelne Controls koennen beim Initialisieren mit false ausgeblendet oder mit true explizit aktiviert werden:
const editor = new ixHTML(element, {
licenseKey: "IXH1-...",
bold: true,
italic: true,
underline: false,
fontSize: true,
table: false,
viewSource: false
});Folgende boolesche Optionen stehen zur Verfuegung:
undo,redobold,italic,underline,strikeThrough,superscript(Hochgesetzt),subscript(Tiefergesetzt)fontName,fontSize,foreColor,hiliteColor,formatBlockjustifyLeft,justifyCenter,justifyRight,justifyFullinsertUnorderedList,insertOrderedListcreateLink,unlink,insertImageinsertHorizontalRule,removeFormattable,specialCharacters,findReplace,viewSource
Die Controls fontName und fontSize verwenden Custom-Dropdowns im gleichen Stil wie das Absatzformat, mit markiertem aktuellem Eintrag und Tastaturbedienung ueber Pfeiltasten, Home/End, Enter und Escape. Die Textauswahl bleibt beim Oeffnen und Auswaehlen erhalten.
Das Dropdown fontSize bietet Schriftgroessen von 8 bis 96 px. Die gewaehlte Groesse wird als CSS-Wert in px im HTML gespeichert und unterstuetzt Undo/Redo. Die aktuelle Groesse wird bei Cursorbewegungen und Textauswahlen angezeigt; bei gemischten Groessen erscheint „Schriftgröße“. Mit fontSize: false wird das Dropdown ausgeblendet. Bei einer eigenen toolbar-Liste muss "fontSize" darin enthalten sein.
Das Control formatBlock zeigt ein eigenes Dropdown fuer Absatz, Ueberschrift 1–6, Zitat und vorformatierten Text (pre, z. B. fuer Code und Text mit festen Abstaenden). Der aktuelle Absatzstil wird beim Klicken in den Text und bei Textselektionen automatisch angezeigt und im Dropdown markiert. Bei unterschiedlichen Stilen innerhalb einer Auswahl erscheint neutral „Absatzformat“. Die Textauswahl bleibt beim Auswaehlen eines Formats erhalten. Das Dropdown laesst sich mit den Pfeiltasten, Home/End, Enter und Escape bedienen.
Der Editor kann am Griff unten rechts groesser und kleiner gezogen werden. Beide Richtungen sind standardmaessig aktiviert und lassen sich beim Initialisieren unabhaengig deaktivieren:
const editor = new ixHTML(element, {
licenseKey: "IXH1-...",
scaleHorizontal: true,
scaleVertical: true
});scaleHorizontal: falsedeaktiviert das Aendern der Breite.scaleVertical: falsedeaktiviert das Aendern der Hoehe.- Sind beide Richtungen deaktiviert, wird der Ziehgriff ausgeblendet.
Der Textfarben-Picker kann beim Initialisieren angepasst werden:
const editor = new ixHTML(element, {
licenseKey: "IXH1-...",
fontColorList: "111827,ef4444,22c55e,3b82f6,7157e8",
fontColorPicking: false
});fontColorListist eine kommaseparierte Liste sechsstelliger Hexcodes ohne#. Sie ersetzt die Standardfarben vollstaendig. MitfontColorList: ""wird die Auswahl der vorgeschlagenen Farben ausgeblendet.fontColorPickingist standardmaessigtrue. Beifalsewerden Farbfläche, Farbton-Regler und Hex-Eingabe ausgeblendet, sodass nur Farben ausfontColorListgewaehlt werden koennen.
Die Texthintergrundfarbe ist standardmaessig als hiliteColor direkt neben der Textfarbe in der Toolbar verfuegbar. Sie faerbt den Hintergrund der ausgewaehlten Zeichen und verwendet denselben Picker mit sofortiger Anwendung, Auswahl-Erhalt und Undo/Redo.
const editor = new ixHTML(element, {
licenseKey: "IXH1-...",
backgroundColorList: "ffff00,bbf7d0,bfdbfe,fecaca",
backgroundColorPicking: false
});backgroundColorList und backgroundColorPicking funktionieren wie die entsprechenden Textfarben-Optionen und lassen sich unabhaengig konfigurieren. Standardmaessig sind freie Farbauswahl und dieselben fuenf vorgeschlagenen Farben wie bei der Textfarbe aktiviert. hiliteColor: false blendet den Button aus; bei einer eigenen toolbar-Liste muss "hiliteColor" aufgenommen werden.
Der Button specialCharacters (Ω) oeffnet eine Tabelle mit 48 gaengigen Sonderzeichen, darunter ©, ®, ™, Waehrungen, Pfeile und mathematische Zeichen. Ein Klick fuegt das Zeichen an der gespeicherten Cursorposition ein oder ersetzt die Textauswahl. Undo/Redo wird unterstuetzt. Mit den Pfeiltasten navigierst du im Raster, mit Enter waehlst du ein Zeichen und mit Escape schliesst du das Dropdown. specialCharacters: false blendet den Button aus; bei einer eigenen toolbar-Liste muss "specialCharacters" enthalten sein.
Der Button insertImage oeffnet einen Dialog zum Einfuegen eines Bildes per URL an der Cursorposition. Das Bild erhaelt width: 50%; max-width: 100%; height: auto und nutzt damit die Haelfte der verfuegbaren Breite der Editor-Instanz bei proportionaler Hoehe. Die Breitenangaben bleiben im ausgegebenen HTML erhalten. Undo/Redo und die vorhandenen Bildfunktionen werden unterstuetzt. insertImage: false blendet den Button aus; bei einer eigenen toolbar-Liste muss "insertImage" enthalten sein.
Mit toolbar werden sowohl die angezeigten Controls als auch deren genaue Reihenfolge festgelegt. separator erzeugt einen visuellen Trenner:
const editor = new ixHTML(element, {
licenseKey: "IXH1-...",
toolbar: [
"formatBlock",
"separator",
"bold",
"italic",
"foreColor",
"hiliteColor",
"separator",
"createLink",
"table",
"separator",
"undo",
"redo"
]
});Nur Eintraege aus diesem Array werden dargestellt. Ist eine aufgefuehrte Funktion gleichzeitig ueber ihre boolesche Option auf false gesetzt, bleibt sie ausgeblendet. Eine Funktion, die nicht im Array steht, wird auch bei true nicht angezeigt.
API
ixHTML.getVersion()liefert die Version der aktuell geladenen Editor-Datei.getHtml()liefert den HTML-Inhalt.setHtml(html)ersetzt den Inhalt.setCssVariable(name, value)undsetCssVariables(values)setzen Designvariablen fuer diese Instanz.setCssRules(rules)ueberschreibt Styles stabiler Editor-Komponenten.getCssVariable(name)undgetCssRules()lesen die wirksame Variable beziehungsweise die gesetzten Komponentenregeln.resetCss()entfernt alle ueber die API gesetzten CSS-Anpassungen.getText()liefert den reinen Text.undo()entfernt das zuletzt abgeschlossene Wort.redo()stellt das zuletzt rueckgaengig gemachte Wort wieder her.focus()fokussiert den Editor.getDarkMode()liefert den aktuellen Darstellungsmodus.setDarkMode(enabled)schaltet den Dark Mode auch nach der Initialisierung um.on(eventName, callback)registriert einen Listener und gibt eine Funktion zum Abmelden zurueck.destroy()entfernt die Editor-Instanz aus dem Container.readywird aufgeloest, sobald die Lizenz erfolgreich geprueft wurde.activateLicense(key)aktiviert eine gesperrte Instanz.getLicenseStatus()liefert Status, Ablaufzeit und Features.refreshLicense()erneuert das signierte Token.deactivateLicense()entfernt die lokale und serverseitige Aktivierung.
Direkt an der Editorinstanz koennen Aenderungen und Statuswechsel beobachtet werden:
const stopListening = editor.on("change", function (event) {
console.log(event.html, event.text);
});
editor.on("input", function (event) {
console.log(event.originalEvent);
});
// Listener spaeter wieder entfernen
stopListening();Unterstuetzt werden input, change, selectionChange, localeChange, darkModeChange, licenseChange und destroy. input wird unmittelbar bei einer Benutzereingabe ausgeloest. change erfasst jede Aenderung am Editorinhalt, also beispielsweise auch setHtml(), Quellcode-Aenderungen, Links sowie Undo/Redo. selectionChange wird beim Bewegen des Cursors oder Aendern der Auswahl ausgeloest und liefert event.selection sowie event.element. Verlaesst die Auswahl den Editor, sind beide Werte null. darkModeChange liefert den neuen Zustand als event.darkMode.
Die Demo in index.html zeigt die direkte Browser-Verwendung.
Plugins schreiben
Ein Plugin ist eine Setup-Funktion, die einmal unter einem eindeutigen Namen registriert wird. Nur Editorinstanzen, die diesen Namen in plugins angeben, laden das Plugin.
Das folgende Plugin zeigt die aktuelle Wortanzahl in der Toolbar:
ixHTML.registerPlugin("wordCount", function (api, options) {
const counter = document.createElement("span");
counter.style.padding = "0 8px";
counter.style.fontSize = "13px";
counter.title = options.label || "Wortanzahl";
api.addFooterControl(counter);
function update() {
const text = api.getText().trim();
const words = text ? text.split(/\s+/).length : 0;
counter.textContent = words + " Woerter";
}
const stopListening = api.on("change", update);
update();
return function cleanup() {
stopListening();
counter.remove();
};
});Das Plugin wird danach beim Erzeugen des Editors aktiviert. Plugin-Optionen sind frei waehlbar und werden unveraendert an das Plugin weitergereicht:
const editor = new ixHTML(document.getElementById("editor"), {
licenseKey: "IXH1-...",
plugins: [
{
name: "wordCount",
options: { label: "Woerter im Dokument" }
}
]
});Ohne Optionen reicht der Plugin-Name:
const editor = new ixHTML(element, {
licenseKey: "IXH1-...",
plugins: ["wordCount"]
});Einen Toolbar-Button hinzufuegen
ixHTML.registerPlugin("insertTimestamp", function (api) {
api.addToolbarButton({
title: "Zeitstempel einfuegen",
icon: "bi-clock",
separatorBefore: true,
onClick: function () {
const value = new Date().toISOString();
api.insertHtml("<time datetime=\"" + value + "\">" + value + "</time>");
}
});
});icon ist eine Bootstrap-Icons-Klasse. Ohne Icon kann mit text kurzer Buttontext angegeben werden. Mit position: "start" wird das Element am Anfang statt am Ende der Toolbar eingefuegt. separatorBefore: true setzt unmittelbar vor dem Plugin-Element einen optischen Trenner.
Alternativ akzeptiert icon ein SVG als Markup, einen DOM-Node oder eine Factory-Funktion. SVG-Markup und DOM-Nodes stammen aus vertrauenswuerdigem Plugin-Code und werden nicht bereinigt:
api.addToolbarButton({
titleKey: "internalLinkTitle",
icon: '<svg viewBox="0 0 24 24" width="18" height="18"><path d="M10 13a5 5 0 0 0 7.5.5l3-3a5 5 0 0 0-7-7l-1.7 1.7"/></svg>',
onClick: openInternalLinkDialog
});Der zurueckgegebene Button-Controller ist weiterhin ein HTMLButtonElement und bietet zusaetzlich setActive() und setDisabled():
const button = api.addToolbarButton({
title: "Interner Link",
icon: "bi-link-45deg",
onClick: openInternalLinkDialog
});
button.setActive(true);
button.setDisabled(false);Die gleiche Option ist bei freien Toolbar- und Footer-Elementen verfuegbar:
api.addToolbarControl(element, {
position: "end",
separatorBefore: true
});
api.addFooterControl(statusElement, {
separatorBefore: true
});Die bisherige Kurzform api.addToolbarControl(element, "start") beziehungsweise "end" bleibt weiterhin gueltig.
Ein Plugin-Modal erstellen
api.createModal() erzeugt einen Dialog im Design des Editors. Felder und Buttons werden deklarativ angegeben:
ixHTML.registerPlugin("insertBadge", function (api) {
const modal = api.createModal({
title: "Hinweis einfuegen",
fields: [
{
name: "text",
label: "Text",
type: "text",
placeholder: "Hinweistext",
required: true
},
{
name: "kind",
label: "Darstellung",
type: "select",
options: [
{ label: "Information", value: "info" },
{ label: "Warnung", value: "warning" }
]
},
{
name: "important",
label: "Wichtig",
type: "checkbox"
}
],
buttons: [
{
label: "Abbrechen",
closeOnClick: true
},
{
label: "Einfuegen",
icon: "bi-check-lg",
submit: true,
closeOnClick: true,
onClick: function (values) {
const badge = document.createElement("span");
badge.className = "badge badge-" + values.kind;
badge.textContent = values.text;
if (values.important) {
badge.setAttribute("data-important", "true");
}
api.insertHtml(badge.outerHTML);
}
}
]
});
api.addToolbarButton({
title: "Hinweis einfuegen",
icon: "bi-info-circle",
separatorBefore: true,
onClick: function () {
modal.open({
text: "",
kind: "info",
important: false
});
}
});
});Unterstuetzte Feldtypen sind text, url, email, number, date, color, textarea, checkbox, select und filepicker. Felder koennen unter anderem value, placeholder, required, min, max, step und autocomplete erhalten. Fuer komplexe Inhalte kann zusaetzlich ein eigenes DOM-Element als content uebergeben werden.
Der zurueckgegebene Modal-Controller bietet:
modal.open(values)undmodal.close()zum Oeffnen und Schliessen.modal.getValue(name)undmodal.setValue(name, value)fuer einzelne Felder.modal.getValues()undmodal.setValues(values)fuer alle Felder.modal.fieldsfuer direkten Zugriff auf die erzeugten Eingabe-Elemente.modal.tabs,modal.getActiveTab()undmodal.setActiveTab(name)fuer Dialoge mit Tabs.modal.elementundmodal.dialogfuer erweiterte DOM-Anpassungen.modal.destroy()zum manuellen Entfernen. Beim Zerstoeren des Editors geschieht dies automatisch.
Ein Button mit submit: true verwendet die normale Formularvalidierung, beispielsweise fuer required. Mit closeOnClick: true wird das Modal nach erfolgreicher Aktion geschlossen. Gibt onClick den Wert false zurueck, bleibt es geoeffnet. closeOnBackdrop: false und closeOnEscape: false koennen das Schliessen ueber Hintergrund beziehungsweise Escape deaktivieren.
Komplexe Plugins koennen Felder in Tabs aufteilen. onChange, onInput und onPaste reagieren direkt auf Feldeingaben. Ein filepicker-Feld verbindet ein URL-Feld mit dem zentral konfigurierten Datei-Callback:
const modal = api.createModal({
title: "Medium einfuegen",
tabs: [
{
name: "general",
title: "Allgemein",
fields: [
{
name: "source",
label: "Quelle",
type: "filepicker",
fileType: "media",
accept: "audio/*,video/*",
onChange: function (value, modal) {
modal.setValue("embed", value ? "<video controls src=\"" + api.encodeHtml(value) + "\"></video>" : "");
}
},
{ name: "width", label: "Breite", type: "number" },
{ name: "height", label: "Hoehe", type: "number" }
]
},
{
name: "embed",
title: "Embed-Code",
fields: [{ name: "embed", label: "HTML", type: "textarea", rows: 7 }]
}
],
buttons: [
{ label: api.t("cancel"), closeOnClick: true },
{
label: api.t("insert"),
submit: true,
closeOnClick: true,
onClick: function (values) {
api.insertHtml(api.sanitizeHtml(String(values.embed || "")));
}
}
]
});Der Controller bietet fuer Tabs zusaetzlich modal.tabs, modal.getActiveTab() und modal.setActiveTab(name).
Plugin-Commands und Menueintraege
Eigene Commands koennen von Toolbar-Buttons, Menueintraegen oder anderem Plugin-Code gemeinsam verwendet werden:
const unregister = api.registerCommand("media.open", function () {
modal.open();
});
api.addToolbarButton({
title: "Medium einfuegen",
icon: "bi-camera-video",
onClick: function () {
api.exec("media.open");
}
});
api.addMenuItem({
menu: "insert",
menuTitle: "Einfuegen",
title: "Medium",
icon: "bi-camera-video",
onClick: function () {
api.exec("media.open");
}
});Mehrere Eintraege mit demselben menu werden in einem gemeinsamen Toolbar-Menue gesammelt. registerCommand() gibt eine Funktion zum Entfernen des Commands zurueck; beim Zerstoeren der Editorinstanz werden registrierte Commands automatisch aufgeraeumt.
URLs, Encoding und HTML-Bereinigung
api.convertUrl(url, context)ruft den zentralenurlConverterauf.contextkann beispielsweise"source"oder"poster"sein.api.encodeHtml(value)escaped einen Wert fuer Text und HTML-Attribute.api.sanitizeHtml(html, policy)entfernt unbekannte Elemente, Event-Attribute und unsichere URL-Protokolle. Inline-Styles, Kommentare und Data-Images sind standardmaessig deaktiviert.api.pickFile(request)oeffnet den konfigurierten Asset-Picker oder ersatzweise die native Browser-Dateiauswahl.
Die Standard-Allowlist von sanitizeHtml() enthaelt auch audio, video, source, track, picture, img und iframe, nicht jedoch script, object oder embed. Eine eigene Policy kann allowedTags, allowedAttributes, allowStyles, allowComments und allowDataImages setzen.
Auswahl lesen und wiederherstellen
api.getSelection() liefert einen Snapshot der aktuellen Auswahl, sofern sie innerhalb des Editors liegt. Ein Snapshot enthaelt eine geklonte native Range, html, text, collapsed und das Element am Anfang der Auswahl. api.saveSelection() speichert den Snapshot zugleich als aktuelle Editor-Auswahl. Damit kann ein Plugin die Auswahl nach dem Fokussieren eines Dialogfelds wiederherstellen:
const selection = api.saveSelection();
dialog.open();
// Nach Abschluss des Dialogs:
if (selection) {
api.restoreSelection(selection);
console.log(api.getSelectedHtml(selection));
console.log(api.getSelectedText(selection));
console.log(api.getSelectedElement(selection));
}Snapshots referenzieren die zum Erstellungszeitpunkt vorhandenen DOM-Nodes. Wurde der Editorinhalt zwischenzeitlich ersetzt, gibt restoreSelection() false zurueck.
Cursor- und Auswahlwechsel lassen sich ohne eigene DOM-Listener beobachten:
const stop = api.on("selectionChange", function (event) {
const internalLink = event.element && event.element.closest("ilink");
button.setActive(Boolean(internalLink && api.editor.contains(internalLink)));
});Direkte DOM-Aenderungen als Transaktion
api.transaction() fuehrt eine synchrone DOM-Aenderung aus und aktualisiert danach automatisch Undo/Redo-History, Quellcode-Spiegel und Toolbar-Zustand. Die Callback-Funktion erhaelt die Plugin-API und ihr Rueckgabewert wird durchgereicht:
api.transaction(function () {
const link = api.getSelectedElement()?.closest("ilink");
if (link && api.editor.contains(link)) {
link.replaceWith(...link.childNodes);
}
});Fuer bereits abgeschlossene direkte DOM-Aenderungen bleibt api.saveHistory() verfuegbar. Asynchrone Arbeit sollte vor der Transaktion abgeschlossen werden; die Transaktionsfunktion selbst ist synchron.
Plugin-API
api.editor,api.rootundapi.toolbargeben Zugriff auf die relevanten DOM-Elemente.api.footerbargibt direkten Zugriff auf die optionale Statusleiste unter dem Editor. Sie ist ohne Inhalt automatisch unsichtbar.api.getHtml(),api.getText()undapi.setHtml(html)lesen oder ersetzen Inhalte.api.getLicenseStatus()liefert den bereits geprueften Lizenzstatus. Plugins starten erst nach erfolgreicher Lizenzpruefung.api.getSelection()undapi.saveSelection()liefern einen Snapshot der aktuellen Editor-Auswahl.api.restoreSelection(snapshot)stellt eine noch gueltige gespeicherte Auswahl wieder her.api.getSelectedHtml(),api.getSelectedText()undapi.getSelectedElement()lesen die aktuelle oder eine uebergebene Auswahl.api.convertUrl(url, context)wendet den konfigurierten URL-Converter an.api.encodeHtml(value)escaped dynamische Text- und Attributwerte.api.sanitizeHtml(html, policy)bereinigt nicht vertrauenswuerdiges HTML anhand einer Allowlist.api.pickFile(request)bindet einen Asset-Manager oder die native Dateiauswahl ein.api.insertHtml(html)fuegt HTML an der aktuellen Cursorposition ein und legt einen Undo/Redo-Schritt an.api.exec(command, value)fuehrt einen vorhandenen Editorbefehl aus.api.registerCommand(name, handler)registriert einen eigenen Plugin-Command.api.transaction(callback)synchronisiert direkte DOM-Aenderungen automatisch mit History und Editor-Oberflaeche.api.addToolbarButton(config)erzeugt einen steuerbaren Button im Design des Editors.api.addMenuItem(config)erzeugt oder erweitert ein Plugin-Menue in der Toolbar.api.addToolbarControl(element, options)nimmt ein beliebiges eigenes DOM-Element in die Toolbar auf.api.addFooterControl(element, options)nimmt ein eigenes Status- oder Anzeigeelement in die Footerbar auf.api.createModal(config)erstellt ein Plugin-Modal mit Feldern, Selects und Aktionsbuttons.api.on("input", callback)reagiert unmittelbar auf Benutzereingaben.api.on("change", callback)reagiert auf jede DOM-Inhaltsaenderung, auch durch Quellcode, Links oder Undo/Redo.api.on("selectionChange", callback)reagiert auf Cursor- und Auswahlwechsel.api.on("licenseChange", callback)reagiert auf eine spaetere Deaktivierung oder erneute Freischaltung.api.on("destroy", callback)reagiert auf das Entfernen der Editorinstanz.api.saveHistory()speichert direkte DOM-Aenderungen eines Plugins als Undo/Redo-Schritt.
Die Setup-Funktion kann eine Cleanup-Funktion oder ein Objekt mit destroy() zurueckgeben. Dieses Cleanup wird automatisch durch editor.destroy() ausgefuehrt. HTML, das ein Plugin mit setHtml oder insertHtml einfuegt, gilt als vertrauenswuerdig; Inhalte von Benutzern oder externen Quellen sollten deshalb vorher mit api.sanitizeHtml() oder einer anwendungsspezifischen Bereinigung verarbeitet werden.
Im Browser muss die Plugin-Datei nach ixhtml.js, aber vor der Editorinitialisierung geladen werden. In einem Bundler wird zuerst ixHTML importiert, danach das Plugin registriert und anschließend die Editorinstanz erzeugt.
