Skip to main content

DHTMLX RichText. Working with different formats example

Live example

RichText reads and writes its content through format encoders. This demo converts between HTML, plain text, and Markdown, with tabs for each format and buttons to load or read it. The encoders go to setValue and getValue.

let areaValue;

// html encoders
const { fromHTML, toHTML } = richtext.html;
// text encoders
const { fromText, toText } = richtext.text;
// markdown encoders
const { fromMarkdown, toMarkdown } = richtext.markdown;

const setters = { html: fromHTML, text: fromText, markdown: fromMarkdown };
const getters = { html: toHTML, text: toText, markdown: toMarkdown };

function trimOuterTags(value) {
    value = value.trim();
    const firstTagEnd = value.indexOf('>');
    const lastTagStart = value.lastIndexOf('<');
    
    if (firstTagEnd !== -1 && lastTagStart > firstTagEnd) {
        return value.substring(firstTagEnd + 1, lastTagStart);
    }
    
    return value;
}

function fromEditor() {
    const active = tabbar.getActive();
    const activeCell = tabbar.getCell(active);
    if (areaValue) areaValue = null;
    activeCell.attachHTML(`<textarea id=${active}>${widget.getValue(getters[active])}</textarea>`);
}

function toEditor() {
    const active = tabbar.getActive();
    const activeCell = tabbar.getCell(active);
    const value = areaValue ? areaValue : trimOuterTags(activeCell.config.html);
    if (areaValue) areaValue = null;
    widget.setValue(value, setters[active]);
}

const widget = new richtext.Richtext("#richtext", { value, imageUploadUrl });
const tabbar = new dhx.Tabbar("tabbar", {
    mode: "top",
    css: "dhx_widget--bordered modes",
    views: [
        { id: "html", tab: "HTML", html: `<textarea id="html">${html}</textarea>` },
        { id: "text", tab: "Text", html: `<textarea id="text">${text}</textarea>` },
        { id: "markdown", tab: "Markdown", html: `<textarea id="markdown">${markdown}</textarea>` },
    ]
});

tabbar.events.on("change", () => (areaValue = null));

document.addEventListener("input", ev => {
    const target = ev.target;

    if (target && (target.id === "html" || target.id === "text" || target.id === "markdown")) {
        areaValue = target.value;
    }
});
<!-- auxiliary controls for interacting with the sample -->
<link rel="stylesheet" href="https://snippet.dhtmlx.com/codebase/assets/css/auxiliary_controls.css">

<section class="dhx_sample-controls">
  <button class="dhx_sample-btn dhx_sample-btn--flat" onclick="fromEditor()">From editor</button>
  <button class="dhx_sample-btn dhx_sample-btn--flat" onclick="toEditor()">To editor</button>
</section>
<div id="richtext" style="width: 100%; height: calc(100% - 360px);"></div>
<div id="tabbar" style="height:300px;"></div>

<style>
.modes textarea {
		display: block;
		width: 100%;
		height: 100%;
        border: none;
        resize: none;
	}
</style>

<script>
const value = "<p>Select mode and try to load data <b>into the editor</b> and <b>back</b></p>";
    const baseURL = "https://docs.dhtmlx.com/richtext-backend";
    const imageUploadUrl = `${baseURL}/images`;

    const html = `
<h2>Rich Text Format</h2>
<p>
The Rich Text Format (often abbreviated RTF) is a proprietary document file format 
with published specification developed by Microsoft Corporation from 1987 until 2008 
for cross-platform document interchange with Microsoft products. Prior to 2008,
Microsoft published updated specifications for RTF with major revisions of Microsoft Word
and Office versions.
</p>`;

    const text = `
Plain text

In computing, plain text is a loose term for data (e.g. file contents) that represent only characters of readable material but not its graphical representation nor other objects (floating-point numbers, images, etc.). It may also include a limited number of "whitespace" characters that affect simple arrangement of text, such as spaces, line breaks, or tabulation characters (although tab characters can "mean" many different things, so are hardly "plain"). Plain text is different from formatted text, where style information is included; from structured text, where structural parts of the document such as paragraphs, sections, and the like are identified; and from binary files in which some portions must be interpreted as binary objects (encoded integers, real numbers, images, etc.).
`;

    const markdown = `
# Markdown

Markdown is a lightweight markup language with plain-text-formatting syntax. Its design allows it to be converted to many output formats, but the original tool by the same name only supports HTML. Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor.

Heading
=======

Sub-heading
-----------

Alternative heading
===

Paragraphs are separated 
by a blank line.

Two spaces at the end of a line  
produce a line break.

Text attributes _italic_, **bold**

Bulleted list:

- item 1
- item 2

Numbered list:

1. item 1
1. item 2

Horizontal rule:

---

> Markdown uses email-style
characters for blockquoting.

Link: [some link](https://docs.dhtmlx.com/richtext-backend/images/3490994099/i.png)

Image:

![DHTMLX](https://docs.dhtmlx.com/richtext-backend/images/3490994099/i.png)
`;
</script>

Use case

Use it when content arrives or leaves in more than one format, like importing a Markdown file, saving plain text to a database, or exporting HTML for an email. RichText reads and writes its content through encoders, so you choose the format at the moment you load or save. The demo wires the conversion to tabs and buttons, but you can call the same methods from your own save flow or import step. To make it yours, pick the encoder for your format and pass it to getValue() when reading or setValue() when writing.

How to do this in your app

  • Read the content: call widget.getValue(encoder). With no encoder it returns HTML; pass richtext.text.toText for plain text.
  • Write the content: call widget.setValue(value, encoder). Pass richtext.text.fromText to load plain text, or omit the encoder to load HTML.
  • Grab the encoders: use richtext.html.toHTML / richtext.html.fromHTML for HTML, richtext.text.toText / richtext.text.fromText for plain text, and richtext.markdown.toMarkdown / richtext.markdown.fromMarkdown for Markdown.
  • Map format to encoder: the demo keeps getters and setters objects that point each tab (html, text, markdown) at its encoder, then looks up the active tab and passes the result to getValue() or setValue().

Good to know

  • HTML is the default format: without an encoder, getValue() returns HTML and setValue() treats the provided content as HTML.
  • Encoders come in pairs: use a to* encoder to read the content out, and a from* encoder to load content in.
  • Conversions can be lossy: plain text removes formatting, while Markdown supports a limited syntax and drops formatting such as fonts, sizes, colors, alignment, and line height. Nested inline structures and nested lists are also limited.
  • The tabs and buttons are just triggers: the surrounding UI only calls getValue() and setValue(). You can drive the same conversion from code with no extra UI.

API reference

Additional resources