WYSIWYG Editor in WordPress Kommentare einbinden (ohne Plugin)

wordpress-kommentare-editor

Standardmäßig kann man WordPress Kommentare nur als reinen Text verfassen. Ich zeige dir, wie du einen WYSIWYG Editor einbauen kannst, damit Besucher ihre Kommentare formatieren können.

Reiner Text reicht manchmal nicht aus, um kompliziertere Anliegen in einem Kommentar auszudrücken, Wörter hervorzuheben oder zu verlinken. Dazu gibt es sogenannte WYSIWYG Editoren (WYSIWYG steht für „What You See Is What You Get“) oder Rich Text Editoren. Diese visuelle Bearbeitung/Formatierung von Text kennst du z.B. von Word oder ähnlichen Programmen. Standardmäßig kann man WordPress Kommentare jedoch nur als reinen Text schreiben.

Mit ein paar Handgriffen bekommst du den Editor auch in deinen Blog!

So sehen WordPress Kommentare standardmäßig aus:

WordPress Standard Kommentarfunktion (reiner Text)
WordPress Standard Kommentarfunktion (reiner Text)

Und so sehen deine WordPress Kommentare in wenigen Minuten aus 😉

 WordPress Kommentarfunktion mit Editor (unser Endgergebnis)
WordPress Kommentarfunktion mit Editor (unser Endgergebnis)

Die beste Demo ist es, wenn Du runter zu den Kommentaren scrollst und es selber ausprobierst. Den Editor habe ich in diesem Beitrag programmiert:

WYSIWYG Editor programmieren

1. Child Theme erstellen

Im ersten Schritt solltest du (falls bereits geschehen springe direkt zu Schritt 2) ein Child Theme deines aktuellen Themes anlegen. Das verhindert, dass nach einem Theme Update alle deine Änderungen wieder weg sind.

  • Navigiere in deinen themes Ordner unter wp-content/themes
  • Lege einen neuen Ordner an, der wie der Ordner deines verwendeten Themes heißt, nur mit der Endung „-child“
    Beispiel: Verwendetes Theme: oceanwp, dann lege einen Ordner oceanwp-child an
  • In dem neu erstellten Ordner erstellst du eine Datei mit dem Namen style.css
  • Füge dort folgenden Inhalt ein und ersetze den Wert bei Template durch deinen Themenamen:
/*
 Theme Name:   Your custom Child theme
 Template:     oceanwp
*/
  • Gehe jetzt im Backend zu Design > Themes und aktiviere das gerade erstellte Theme aus. In diesem Fall gibt es dort den Eintrag „Your custom Child theme“.

2. Standard Kommentar Editor entfernen

Um den alten WordPress Kommentar Editor zu entfernen müssen wir die comments.php bearbeiten. Kopiere diese Datei dazu aus deinem Themes Ordner in dein Child-Theme.

Suche dort nach einer Zeile, die das Wort comment_field beinhaltet. So ähnlich sollte das bei dir aussehen:

// [...]
// Comment form args.
$args = array(
    /* translators: 1: logged in to comment */
    'must_log_in'          => '<p class="must-log-in">' . sprintf(esc_html__('You must be %1$slogged in%2$s to post a comment.', 'oceanwp'), '[...],
    'logged_in_as'         => '<p class="logged-in-as">' . esc_html__('Logged in as', 'oceanwp') . ' <a href="' . admin_url('profile.php') . '[...],
    'comment_notes_before' => false,
    'comment_notes_after'  => false,
    // 'comment_field'        => '<div class="comment-textarea"><label for="comment" class="screen-reader-text">' . esc_html__('Comment', 'ocea[...],
    'id_submit'            => 'comment-submit',
    'label_submit'         => esc_html(oceanwp_theme_strings('owp-string-comment-post-button', false, 'oceanwp')),
);
// [...]

Kommentiere die entsprechende Zeile aus, wie im Beispiel gezeigt.

3. Neuen Kommentar Editor in WordPress anlegen & anpassen

In einem anderen Beitrag habe ich gezeigt, wie man mit HTML, CSS und JavaScript einen eigenen WYSIWYG Editor programmieren kann. Diesen wollen wir hier verwenden.

Außerdem verwenden wir einen WordPress Filter comment_form_defaults. Damit können wir die Variable comment_field wieder befüllen, die wir im letzten Schritt auskommentiert haben.

Dazu packe ich alle Komponenten zusammen in eine Datei mit dem Namen wysiwyg-comments.php und lege sie ins Child Theme:

<?php

add_filter('comment_form_defaults', 'wysiwyg_webdeasy_editor');
function wysiwyg_webdeasy_editor($args) {
    ob_start();
?>

    <div class="wp-webdeasy-comment-editor">
        <div class="toolbar">
            <div class="line">
                <div class="box">
                    <span class="editor-btn icon smaller" data-action="bold" data-tag-name="b" title="Bold">
                        <img src="https://img.icons8.com/fluency-systems-filled/48/000000/bold.png" />
                    </span>
                    <span class="editor-btn icon smaller" data-action="italic" data-tag-name="i" title="Italic">
                        <img src="https://img.icons8.com/fluency-systems-filled/48/000000/italic.png" />
                    </span>
                    <span class="editor-btn icon smaller" data-action="underline" data-tag-name="u" title="Underline">
                        <img src="https://img.icons8.com/fluency-systems-filled/48/000000/underline.png" />
                    </span>
                    <span class="editor-btn icon smaller" data-action="strikeThrough" data-tag-name="strike" title="Strike through">
                        <img src="https://img.icons8.com/fluency-systems-filled/30/000000/strikethrough.png" />
                    </span>
                </div>
                <div class="box">
                    <span class="editor-btn icon has-submenu">
                        <img src="https://img.icons8.com/fluency-systems-filled/48/000000/align-left.png" />
                        <div class="submenu">
                            <span class="editor-btn icon" data-action="justifyLeft" data-style="textAlign:left" title="Justify left">
                                <img src="https://img.icons8.com/fluency-systems-filled/48/000000/align-left.png" />
                            </span>
                            <span class="editor-btn icon" data-action="justifyCenter" data-style="textAlign:center" title="Justify center">
                                <img src="https://img.icons8.com/fluency-systems-filled/48/000000/align-center.png" />
                            </span>
                            <span class="editor-btn icon" data-action="justifyRight" data-style="textAlign:right" title="Justify right">
                                <img src="https://img.icons8.com/fluency-systems-filled/48/000000/align-right.png" />
                            </span>
                            <span class="editor-btn icon" data-action="formatBlock" data-style="textAlign:justify" title="Justify block">
                                <img src="https://img.icons8.com/fluency-systems-filled/48/000000/align-justify.png" />
                            </span>
                        </div>
                    </span>
                    <span class="editor-btn icon" data-action="insertOrderedList" data-tag-name="ol" title="Insert ordered list">
                        <img src="https://img.icons8.com/fluency-systems-filled/48/000000/numbered-list.png" />
                    </span>
                    <span class="editor-btn icon" data-action="insertUnorderedList" data-tag-name="ul" title="Insert unordered list">
                        <img src="https://img.icons8.com/fluency-systems-filled/48/000000/bulleted-list.png" />
                    </span>
                    <span class="editor-btn icon" data-action="outdent" title="Outdent" data-required-tag="li">
                        <img src="https://img.icons8.com/fluency-systems-filled/48/000000/outdent.png" />
                    </span>
                    <span class="editor-btn icon" data-action="indent" title="Indent">
                        <img src="https://img.icons8.com/fluency-systems-filled/48/000000/indent.png" />
                    </span>
                </div>
                <div class="box">
                    <span class="editor-btn icon" data-action="insertHorizontalRule" title="Insert horizontal rule">
                        <img src="https://img.icons8.com/fluency-systems-filled/48/000000/horizontal-line.png" />
                    </span>
                </div>
            </div>
            <div class="line">
                <div class="box">
                    <span class="editor-btn icon smaller" data-action="undo" title="Undo">
                        <img src="https://img.icons8.com/fluency-systems-filled/48/000000/undo--v1.png" />
                    </span>
                    <span class="editor-btn icon" data-action="removeFormat" title="Remove format">
                        <img src="https://img.icons8.com/fluency-systems-filled/48/000000/remove-format.png" />
                    </span>
                </div>
                <div class="box">
                    <span class="editor-btn icon smaller" data-action="createLink" title="Insert Link">
                        <img src="https://img.icons8.com/fluency-systems-filled/48/000000/add-link.png" />
                    </span>
                    <span class="editor-btn icon smaller" data-action="unlink" data-tag-name="a" title="Unlink">
                        <img src="https://img.icons8.com/fluency-systems-filled/48/000000/delete-link.png" />
                    </span>
                </div>
                <div class="box">
                    <span class="editor-btn icon" data-action="toggle-view" title="Show HTML-Code">
                        <img src="https://img.icons8.com/fluency-systems-filled/48/000000/source-code.png" />
                    </span>
                </div>
            </div>
        </div>
        <div class="content-area">
            <div class="visuell-view" contenteditable></div>
            <textarea class="html-view" id="comment" name="comment"></textarea>
        </div>
    </div>
    <div class="modal">
        <div class="modal-bg"></div>
        <div class="modal-wrapper">
            <div class="close">✖</div>
            <div class="modal-content" id="modalCreateLink">
                <h3>Insert Link</h3>
                <input type="text" id="linkValue" placeholder="Link (example: https://webdeasy.de/)" />
                <div class="row">
                    <input type="checkbox" id="new-tab" />
                    <label for="new-tab">Open in new Tab?</label>
                </div>
                <button class="done">Done</button>
            </div>
        </div>
    </div>
    <script>
        // define vars
        const editor = document.getElementsByClassName('wp-webdeasy-comment-editor')[0];
        const toolbar = editor.getElementsByClassName('toolbar')[0];
        const buttons = toolbar.querySelectorAll('.editor-btn:not(.has-submenu)');
        const contentArea = editor.getElementsByClassName('content-area')[0];
        const visuellView = contentArea.getElementsByClassName('visuell-view')[0];
        const htmlView = contentArea.getElementsByClassName('html-view')[0];
        const modal = document.getElementsByClassName('modal')[0];

        // add active tag event
        document.addEventListener('selectionchange', selectionChange);

        // add paste event
        visuellView.addEventListener('paste', pasteEvent);

        // add paragraph tag on new line
        contentArea.addEventListener('keypress', addParagraphTag);

        // add toolbar button actions
        for (let i = 0; i < buttons.length; i++) {
            let button = buttons[i];

            button.addEventListener('click', function(e) {
                let action = this.dataset.action;

                switch (action) {
                    case 'toggle-view':
                        execCodeAction(this, editor);
                        break;
                    case 'createLink':
                        execLinkAction();
                        break;
                    default:
                        execDefaultAction(action);
                }

            });
        }

        /** 
         * This function toggles between visual and html view
         */
        function execCodeAction(button, editor) {

            if (button.classList.contains('active')) { // show visuell view
                visuellView.innerHTML = htmlView.value;
                htmlView.style.display = 'none';
                visuellView.style.display = 'block';

                button.classList.remove('active');
            } else { // show html view
                htmlView.innerText = visuellView.innerHTML;
                visuellView.style.display = 'none';
                htmlView.style.display = 'block';

                button.classList.add('active');
            }
        }

        /**
         * This function adds a link to the current selection
         */
        function execLinkAction() {
            modal.style.display = 'block';
            let selection = saveSelection();

            let submit = modal.querySelectorAll('button.done')[0];
            let close = modal.querySelectorAll('.close')[0];

            // done button active => add link
            submit.addEventListener('click', function(e) {
                e.preventDefault();
                let newTabCheckbox = modal.querySelectorAll('#new-tab')[0];
                let linkInput = modal.querySelectorAll('#linkValue')[0];
                let linkValue = linkInput.value;
                let newTab = newTabCheckbox.checked;

                restoreSelection(selection);

                if (window.getSelection().toString()) {
                    let a = document.createElement('a');
                    a.href = linkValue;
                    if (newTab) a.target = '_blank';
                    window.getSelection().getRangeAt(0).surroundContents(a);
                }

                modal.style.display = 'none';
                linkInput.value = '';

                // deregister modal events
                submit.removeEventListener('click', arguments.callee);
                close.removeEventListener('click', arguments.callee);
            });

            // close modal on X click
            close.addEventListener('click', function(e) {
                e.preventDefault();
                let linkInput = modal.querySelectorAll('#linkValue')[0];

                modal.style.display = 'none';
                linkInput.value = '';

                // deregister modal events
                submit.removeEventListener('click', arguments.callee);
                close.removeEventListener('click', arguments.callee);
            });
        }

        /**
         * This function executes all 'normal' actions
         */
        function execDefaultAction(action) {
            document.execCommand(action, false);
        }

        /**
         * Saves the current selection
         */
        function saveSelection() {
            if (window.getSelection) {
                sel = window.getSelection();
                if (sel.getRangeAt && sel.rangeCount) {
                    let ranges = [];
                    for (var i = 0, len = sel.rangeCount; i < len; ++i) {
                        ranges.push(sel.getRangeAt(i));
                    }
                    return ranges;
                }
            } else if (document.selection && document.selection.createRange) {
                return document.selection.createRange();
            }
            return null;
        }

        /**
         *  Loads a saved selection
         */
        function restoreSelection(savedSel) {
            if (savedSel) {
                if (window.getSelection) {
                    sel = window.getSelection();
                    sel.removeAllRanges();
                    for (var i = 0, len = savedSel.length; i < len; ++i) {
                        sel.addRange(savedSel[i]);
                    }
                } else if (document.selection && savedSel.select) {
                    savedSel.select();
                }
            }
        }

        /**
         * Sets the current selected format buttons active/inactive
         */
        function selectionChange(e) {

            for (let i = 0; i < buttons.length; i++) {
                let button = buttons[i];

                // don't remove active class on code toggle button
                if (button.dataset.action === 'toggle-view') continue;

                button.classList.remove('active');
            }

            if (!childOf(window.getSelection().anchorNode.parentNode, editor)) return false;

            parentTagActive(window.getSelection().anchorNode.parentNode);
        }

        /**
         * Checks if the passed child has the passed parent
         */
        function childOf(child, parent) {
            return parent.contains(child);
        }

        /**
         * Sets the tag active that is responsible for the current element
         */
        function parentTagActive(elem) {
            if (!elem || !elem.classList || elem.classList.contains('visuell-view')) return false;

            let toolbarButton;

            // active by tag names
            let tagName = elem.tagName.toLowerCase();
            toolbarButton = document.querySelectorAll(`.toolbar .editor-btn[data-tag-name="${tagName}"]`)[0];
            if (toolbarButton) {
                toolbarButton.classList.add('active');
            }

            // active by text-align
            let textAlign = elem.style.textAlign;
            toolbarButton = document.querySelectorAll(`.toolbar .editor-btn[data-style="textAlign:${textAlign}"]`)[0];
            if (toolbarButton) {
                toolbarButton.classList.add('active');
            }

            return parentTagActive(elem.parentNode);
        }

        /**
         * Handles the paste event and removes all HTML tags
         */
        function pasteEvent(e) {
            e.preventDefault();

            let text = (e.originalEvent || e).clipboardData.getData('text/plain');
            document.execCommand('insertHTML', false, text);
        }

        /**
         * This functions adds a paragraph tag when the enter key is pressed
         */
        function addParagraphTag(evt) {
            if (evt.keyCode == '13') {

                // don't add a p tag on list item
                if (window.getSelection().anchorNode.parentNode.tagName === 'LI') return;
                document.execCommand('formatBlock', false, 'p');
            }
        }

        /**
         * On WordPress comment form submit: set last content to html view
         */
        const commentForm = document.getElementById('commentform');
        commentForm.addEventListener('submit', function(e) {
            htmlView.innerText = visuellView.innerHTML;
        });
    </script>
    <style>
        /* WYSIWYG Editor */
        .wp-webdeasy-comment-editor {
            width: 100%;
            min-height: 30rem;
            box-shadow: 0 0 4px 1px rgba(0, 0, 0, 0.3);
            border-top: 6px solid #4a4a4a;
            border-radius: 3px;
            margin: 2rem 0;
        }

        .wp-webdeasy-comment-editor .toolbar {
            box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
        }

        .wp-webdeasy-comment-editor .toolbar .line {
            display: flex;
            border-bottom: 1px solid #e2e2e2;
        }

        .wp-webdeasy-comment-editor .toolbar .line:last-child {
            border-bottom: none;
        }

        .wp-webdeasy-comment-editor .toolbar .line .box {
            display: flex;
            border-left: 1px solid #e2e2e2;
        }

        .wp-webdeasy-comment-editor .toolbar .line .box .editor-btn {
            display: block;
            display: flex;
            align-items: center;
            justify-content: center;
            position: relative;
            transition: 0.2s ease all;
        }

        .wp-webdeasy-comment-editor .toolbar .line .box .editor-btn:hover,
        .wp-webdeasy-comment-editor .toolbar .line .box .editor-btn.active {
            background-color: #e1e1e1;
            cursor: pointer;
        }

        .wp-webdeasy-comment-editor .toolbar .line .box .editor-btn.icon img {
            width: 15px;
            padding: 9px;
            box-sizing: content-box;
        }

        .wp-webdeasy-comment-editor .toolbar .line .box .editor-btn.icon.smaller img {
            width: 16px;
        }

        .wp-webdeasy-comment-editor .toolbar .line .box .editor-btn.has-submenu {
            width: 20px;
            padding: 0 10px;
        }

        .wp-webdeasy-comment-editor .toolbar .line .box .editor-btn.has-submenu::after {
            content: '';
            width: 6px;
            height: 6px;
            position: absolute;
            background-image: url(https://img.icons8.com/ios-glyphs/30/000000/chevron-down.png);
            background-repeat: no-repeat;
            background-size: cover;
            background-position: center;
            right: 4px;
        }

        .wp-webdeasy-comment-editor .toolbar .line .box .editor-btn.has-submenu .submenu {
            display: none;
            position: absolute;
            top: 34px;
            left: -1px;
            z-index: 10;
            background-color: #FFF;
            border: 1px solid #b5b5b5;
            border-top: none;
        }

        .wp-webdeasy-comment-editor .toolbar .line .box .editor-btn.has-submenu .submenu .btn {
            width: 39px;
        }

        .wp-webdeasy-comment-editor .toolbar .line .box .editor-btn.has-submenu .submenu:hover {
            display: block;
        }

        .wp-webdeasy-comment-editor .toolbar .line .box .editor-btn.has-submenu:hover .submenu {
            display: block;
        }

        .wp-webdeasy-comment-editor .content-area {
            padding: 15px 12px;
            line-height: 1.5;
        }

        .wp-webdeasy-comment-editor .content-area .visuell-view {
            outline: none;
            min-height: 12rem;
        }

        .wp-webdeasy-comment-editor .content-area .visuell-view p {
            margin: 12px 0;
        }

        .wp-webdeasy-comment-editor .content-area .html-view {
            outline: none;
            display: none;
            width: 100%;
            height: 200px;
            border: none;
            resize: none;
        }

        /* Modal */
        .modal {
            z-index: 40;
            display: none;
        }

        .modal .modal-wrapper {
            background-color: #FFF;
            padding: 1rem;
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 20rem;
            min-height: 10rem;
            z-index: 41;
        }

        .modal .modal-wrapper .close {
            position: absolute;
            top: 1rem;
            right: 1rem;
            cursor: pointer;
        }

        .modal .modal-wrapper .modal-content {
            flex-direction: column;
        }

        .modal .modal-wrapper .modal-content h3 {
            margin-top: 0;
        }

        .modal .modal-wrapper .modal-content input {
            margin: 1rem 0;
            padding: 0.5rem;
        }

        .modal .modal-wrapper .modal-content input[type="text"] {
            width: calc(100% - 1rem);
        }

        .modal .modal-wrapper .modal-content .row label {
            margin-left: 0.5rem;
        }

        .modal .modal-wrapper .modal-content button {
            background-color: #D2434F;
            border: 0;
            color: #FFF;
            padding: 0.5rem 1.2rem;
            cursor: pointer;
        }

        .modal .modal-bg {
            position: fixed;
            background-color: rgba(0, 0, 0, 0.3);
            width: 100vw;
            height: 100vh;
            top: 0;
            left: 0;
        }
    </style>

<?php
    $args['comment_field'] = ob_get_clean();
    return $args;
}

Nun erstellen wir im Child-Theme noch eine functions.php und rufen dort unsere eben erstellte wysiwyg-comments.php auf:

<?php
require_once(__DIR__ . "/wysiwyg-comments.php");

Das ist 1:1 der Code aus dem WYSIWYG Editor Tutorial. Daher müssen wir noch drei kleine Anpassungen machen, damit er in WordPress ordentlich funktioniert.

Beachte, dass der Editor Icons von Icons8 verwendet und du einen entsprechenden Hinweis auf deiner Website vermerken musst.

3.1 Editor Styling anpassen

Ich möchte den Editor volle Breite einnehmen lassen und auch etwas höher haben. Deshalb dieser Code. Du kannst natürlich alles deinen Wünschen entsprechend anpassen, so dass der Editor zu den Kommentaren auf deinem Blog oder deiner Website passt.

Die genaue Stelle habe ich im kompletten Code oben rot hervorgehoben.

.wp-webdeasy-comment-editor {
  width: 100%;  // before: 40rem
  min-height: 30rem; // before: 18rem
  box-shadow: 0 0 4px 1px rgba(0, 0, 0, 0.3);
  border-top: 6px solid #4a4a4a;
  border-radius: 3px;
  margin: 2rem 0;
}

3.2 HTML IDs an WordPress anpassen

Das Textarea unserer HTML Ansicht benötigt die zusätzlichen Attribute name und id. Im kompletten Code oben habe ich die Stelle hervorgehoben.

<!-- Add 'name' and 'id' attribute -->
<textarea class="html-view" id="comment" name="comment"></textarea>

3.3 HTML Inhalt beim Formular absenden übergeben

Damit WordPress auch den Inhalt aus unserem Editor verwendet, muss beim Absenden des Formulars unser HTML Inhalt in das von WordPress verwendete Textfeld gesetzt werden.

Dazu muss dieser Code muss an die farblich hervorgehobene Stelle im Code eingefügt werden:

/**
 * On WordPress comment form submit: set last content to html view
 */
const commentForm = document.getElementById('commentform');
commentForm.addEventListener('submit', function(e) {
  htmlView.innerText = visuellView.innerHTML;
});

Wenn du willst, kannst Du das JavaScript und CSS in einzelne Dateien verpacken und per wp_enqueue_script() und wp_enqueue_style() in der functions.php einbinden.

Das war’s auch schon. Der Editor ist einsatzbereit! 🙂 Wenn es Probleme gab, schreib‘ mir gern einen Kommentar.

Ich habe einen kompletten Beitrag zu ganz vielen interessanten WordPress Code Snippets, die deine Seite mit wenig Aufwand verbessern können. Vielleicht ist das für dich interessant.

Ähnliche Beiträge
Beteilige dich an der Unterhaltung

2 Kommentare

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

bold italic underline strikeThrough
insertOrderedList insertUnorderedList outdent indent
removeFormat
createLink unlink
code

Das könnte dich auch interessieren