Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

nhayder's avatar
Level 13

Building Tiny Text Editor With Vue? VUE expert advice needed

Hey,

Im working on a tiny text editor build with vue, im in phase of researching and organizing the workflow.

what im trying to achieve is to allow the user to click title and edit text,

Please have a look at this image https://www.dropbox.com/s/g1oa0wpszdw64eu/Screen%20Shot%202018-09-27%20at%2010.51.41%20AM.png?dl=0

i can populate the menu using poper.js and attached some html icons، What is confusing me is how to make the h1 tags editable ????

According to some tutorials, they basically replace the H1 content with borderless textarea element on the page, and after saving the data? Vue Will replace the texteditor with h1 tags to return the user to initial editing state.

Is this the best way to build this type of editor ???

  • Is there is any plugin that dos the same so i can use it.
  • Can you show basic Vue code to for editing the H1 main title.

all the best

0 likes
5 replies
MaverickChan's avatar

it is very hard . For example

make a div contenteditable , or using the textarea ,building with basic execCommand is easy , you will have bold , italic , and other format going .

but , getting fonts , link , image upload ,insert media ... those will cause tons of lines of code...

you can get some vue components on github.

i used to try to build one , took me a lot of time ....

1 like
nhayder's avatar
Level 13

@MaverickChan Font + images + uploads images are not required at all, its a small editor. Basically i need bold, italic, text left, text right and text center.

That's all.

Do you recommend using VUE for this type of project?

MaverickChan's avatar
Level 47

using vue is a easy way to manipulate data and functions , in fact , a wysiwyg editor is a html thing or plus little js.

btw , your image is a editor called medium? or i remember wrong? is not vue based.

this is what i do , you can change it . you can import anywhere in your html

<template>

    <div class="mx-5 my-3">
        
        <div v-for="(item, index) in commands" :key="index" class="btn-group" :class="{ 'border-right':borderRight(index)}">

            <button type="button" class="btn btn-sm btn-outline-secondary border-0 rounded-0" data-toggle="tooltip" data-placement="bottom" :title="item.title" @click="exec(item.command)">

                <i :class="'fa ' + item.icon"></i>

            </button>           

        </div>




        <div class="editor border mt-2" id="editor" contenteditable="true" @mouseup="getCurrentTagName">
            


        </div>

        <div class="mt-3">
            
            <button class="btn btn-outline-secondary rounded-0 border" @click="clear">Clear</button>

        </div>

    </div>


</template>

<script>



export default {

    name: 'wysiwyg',

    mounted () {

        $(function () {

            $('[data-toggle="tooltip"]').tooltip()

        })

    },

    data () {

        return {

            commands : [

                { name: 'Bold', title: 'Bold', command: 'bold', icon: 'fa-bold' },

                { name: 'Italic', title: 'Italic', command: 'italic', icon: 'fa-italic' },

                { name: 'StrikeThrough', title: 'Strike Through', command: 'strikeThrough', icon: 'fa-strikethrough' },

                { name: 'Underline', title: 'Underline', command: 'underline', icon: 'fa-underline' },

                { name: 'HorizontalLine', title: 'Horizontal Line', command: 'insertHorizontalRule', icon: 'fa-minus' },

                { name: 'AlignLeft', title: 'Align Left', command: 'justifyLeft', icon: 'fa-align-left' },

                { name: 'AlignJustify', title: 'Align Justify', command: 'justifyFull', icon: 'fa-align-justify' },

                { name: 'AlignCenter', title: 'Align Center', command: 'justifyCenter', icon: 'fa-align-center' },

                { name: 'AlignRight', title: 'Align Right', command: 'justifyRight', icon: 'fa-align-right' },

                { name: 'FontSize', title: 'Font Size', command: 'fontSize', icon: 'fa-text-height' },

                { name: 'Fonts', title: 'Fonts', command: 'fontName', icon: 'fa-font' },

                { name: 'Heading', title: 'Heading', command: 'header', icon: 'fa-header' },

                { name: 'Paragraph', title: 'Paragraph', command: 'insertParagraph', icon: 'fa-paragraph' },

                { name: 'Undo', title: 'Undo', command: 'undo', icon: 'fa-undo' },

                { name: 'Redo', title: 'Redo', command: 'redo', icon: 'fa-repeat' },

                { name: 'Copy', title: 'Copy', command: 'copy', icon: 'fa-copy' },

                { name: 'Paste', title: 'Paste', command: 'paste', icon: 'fa-paste' },

                { name: 'Cut', title: 'Cut', command: 'cut', icon: 'fa-cut' },

                { name: 'Subscript', title: 'Subscript', command: 'subscript', icon: 'fa-subscript' },

                { name: 'Superscript', title: 'Superscript', command: 'superscript', icon: 'fa-superscript' },

                { name: 'OrderedList', title: 'Ordered List', command: 'insertOrderedList', icon: 'fa-list-ol' },

                { name: 'UnorderdList', title: 'Unorderd List', command: 'insertUnorderedList', icon: 'fa-list-ul' },

                { name: 'Link', title: 'Link', command: 'createLink', icon: 'fa-link' },

                { name: 'Unlink', title: 'Unlink', command: 'unlink', icon: 'fa-unlink' },

                { name: 'Video', title: 'Video', command: 'video', icon: 'fa-video-camera' },

                { name: 'Image', title: 'Image', command: 'insertImage', icon: 'fa-image' },

                { name: 'RemoveFormat', title: 'Remove Format', command: 'removeFormat', icon: 'fa-times' },


            ],

            currentTagName: '',




        }

    },

    watch : {

        currentTagName () {

            this.getCurrentTagName()

        }

    },

    methods : {

        exec (command,arg) {

            document.execCommand(command , false , arg)

        },

        clear () {

            document.getElementById("editor").innerHTML = ''

        },

        borderRight (index) {

            var clubs = [

                5,9,13,15,18,20,22,24,26

            ]

            return !!clubs.includes(index + 1)

        },

        getCurrentTagName () {

            if (window.getSelection().baseNode) {

                this.currentTagName = window.getSelection().baseNode.parentNode.tagName                 

            }

        }

    }

}
</script>

<style lang="scss" scoped>

    .editor {

        min-height: 20em;

        padding: 1em;


    }

    .btn:hover {

        background: lighten(black, 90%);

        color: lighten(black, 10%);

    }

    [contenteditable]:focus {

        outline: none;

    }

    [contenteditable] {

        font-size: 120%;

    }

</style>
1 like
nhayder's avatar
Level 13

@MaverickChan Thank you for the time and efforts you spent typing the code, it looks like your code full working on my app. (I'M SAVED :-) )

Although i need to make some adjustment to it but at least i have stating point.

Thank you again,

Please or to participate in this conversation.