Use execCommand to set the text style to CSS style

preface

Yesterday, while addressing the need to add more font sizes to the rich text editor Umeditor, I discovered a very interesting API called execCommand. This API allows us to manipulate content in rich text. For more information on this execCommand, see how you can add more font size to the Umeditor component based on this introduction

Problem description

From looking at the Umeditor documentation, I’m sure you know that adding a size to the editor is as simple as adding a line of code to the umeditor.config.js configuration file, like this:

fontsize: [ 12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.60.72.96]
Copy the code

However, after ADDING, I found the following bugs:

The input size is displayed correctly, but I want to change the size of some words after typing, so the set size is not displayed correctly, as follows:

So I started to look at the source code and found the key code as follows:

The source code has the default font size:

    ///import core
    ///import plugins\removeformat.js
    /// Commands font color, background color, size, font, underline, strikeout
    ///commandsName ForeColor,BackColor,FontSize,FontFamily,Underline,StrikeThrough
    CommandsTitle Font color, background color, size, font, underline, strikeout
    / * * *@description Font *@name UM.execCommand
     * @param {String}     CmdName Specifies the function name *@param {String}    Value Indicates the incoming value */
    UM.plugins['font'] = function () {
        var me = this,
            fonts = {
                'forecolor': 'forecolor'.'backcolor': 'backcolor'.'fontsize': 'fontsize'.'fontfamily': 'fontname'
            },
            cmdNameToStyle = {
                'forecolor': 'color'.'backcolor': 'background-color'.'fontsize': 'font-size'.'fontfamily': 'font-family'
            },
            cmdNameToAttr = {
                'forecolor': 'color'.'fontsize': 'size'.'fontfamily': 'face'
            };
        me.setOpt({
            'fontfamily': [{
                    name: 'songti'.val: 'song typeface, SimSun'
                },
                {
                    name: 'yahei'.val: 'Microsoft YaHei'
                },
                {
                    name: 'kaiti'.val: 'Kai, Kai _GB2312, SimKai'
                },
                {
                    name: 'heiti'.val: 'Black body, SimHei'
                },
                {
                    name: 'lishu'.val: 'Li Shu, SimLi'
                },
                {
                    name: 'andaleMono'.val: 'andale mono'
                },
                {
                    name: 'arial'.val: 'arial, helvetica,sans-serif'
                },
                {
                    name: 'arialBlack'.val: 'arial black,avant garde'
                },
                {
                    name: 'comicSansMs'.val: 'comic sans ms'
                },
                {
                    name: 'impact'.val: 'impact,chicago'
                },
                {
                    name: 'timesNewRoman'.val: 'times new roman'
                },
                {
                    name: 'sans-serif'.val: 'sans-serif'}].// 'fontsize': [10, 12, 16, 18, 24, 32, 48]
            'fontsize': [ 8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.60.72.96]}); me.addOutputRule(function (root) {
            utils.each(root.getNodesByTagName('font'), function (node) {
                if (node.tagName == 'font') {
                    var cssStyle = [];
                    for (var p in node.attrs) {
                        switch (p) {
                            case 'size':
                                console.log(node.attrs)
                                var val = node.attrs[p];
                                console.log('dddd333', val);
                                $.each(
                                    / / {
                                    / / '10' : '1',
                                    / / '12', '2',
                                    / / '16', '3',
                                    / / '18' : '4',
                                    / / '24' : '5',
                                    / / '32', '6',
                                    / / ', 48 ', '7'
                                    // },
                                    {
                                        '8': '1'.'9': '2'.'10': '3'.'11': '4'.'12': '5'.'13': '6'.'14': '7'.'15': '8'.'16': '9'.'17': '10'.'18': '11'.'the': '12'.'20': '13'.'21': '14'.'22': '15'.'23': '16'.'24': '17'.'25': '18'.'26': 'the'.'27': '20'.'28': '21'.'and': '22'.'30': '23'.'and': '24'.'32': '25'.'33': '26'.'34': '27'.'35': '28'.'36': 'and'.'37': '30'.'38': 'and'.'and': '32'.'40': '33'.'and': '34'.The '42': '35'.'43': '36'.'44': '37'.'45': '38'.'46': 'and'.'47': '40'.', 48 ': 'and'.'60': The '42'.'72': '43'.'96': '44',},function (k, v) {
                                        console.log('dddddd44',k, v)
                                        if (v == val) {
                                            val = k;
                                            return false; }});console.log('dddddddddddddddddddddfont-size:',val)
                                cssStyle.push('font-size:' + val + 'px');
                                break;
                            case 'color':
                                cssStyle.push('color:' + node.attrs[p]);
                                break;
                            case 'face':
                                cssStyle.push('font-family:' + node.attrs[p]);
                                break;
                            case 'style':
                                cssStyle.push(node.attrs[p]);
                        }
                    }
                    node.attrs = {
                        'style': cssStyle.join('; ')}; } node.tagName ='span';
                if (node.parentNode.tagName == 'span' && node.parentNode.children.length == 1) {
                    $.each(node.attrs, function (k, v) {

                        node.parentNode.attrs[k] = k == 'style' ? node.parentNode.attrs[k] + v : v;
                    })
                    node.parentNode.removeChild(node, true); }}); });for (var p in fonts) {
            (function (cmd) {
                me.commands[cmd] = {
                    execCommand: function (cmdName, value) {
                        console.log('d6',value)
                        if (value == 'transparent') {
                            return;
                        }
                        var rng = this.selection.getRange();
                        if (rng.collapsed) {
                            var span = $('<span></span>').css(cmdNameToStyle[cmdName], value)[0];
                            rng.insertNode(span).setStart(span, 0).setCursor();
                        } else {
                            if (cmdName == 'fontsize') {
                                // value = {
                                / / '10' : '1',
                                / / '12', '2',
                                / / '16', '3',
                                / / '18' : '4',
                                / / '24' : '5',
                                / / '32', '6',
                                //     '48': '7'
                                // }
                                value = {
                                    '8': '1'.'9': '2'.'10': '3'.'11': '4'.'12': '5'.'13': '6'.'14': '7'.'15': '8'.'16': '9'.'17': '10'.'18': '11'.'the': '12'.'20': '13'.'21': '14'.'22': '15'.'23': '16'.'24': '17'.'25': '18'.'26': 'the'.'27': '20'.'28': '21'.'and': '22'.'30': '23'.'and': '24'.'32': '25'.'33': '26'.'34': '27'.'35': '28'.'36': 'and'.'37': '30'.'38': 'and'.'and': '32'.'40': '33'.'and': '34'.The '42': '35'.'43': '36'.'44': '37'.'45': '38'.'46': 'and'.'47': '40'.', 48 ': 'and'.'60': The '42'.'72': '43'.'96': '44'
                                }
                                [(value + "").replace(/px/.' ')]
                                console.log('d7:',value)
                            }
                            / / TODO execCommand currently only support 1-7 https://www.yuque.com/trisolar/odtpgb/tv6wyd
                            this.document.execCommand(fonts[cmdName], false, value);
                            console.log('d8:', fonts[cmdName])
                            console.log('d9:', value)
                            console.log('browser.gecko:', browser.gecko)
                            console.log('! browser.ie:',browser.gecko)
                            if (browser.gecko) {
                                $.each(this.$body.find('a'), function (i, a) {
                                    var parent = a.parentNode;
                                    if (parent.lastChild === parent.firstChild && /FONT|SPAN/.test(parent.tagName)) {
                                        var cloneNode = parent.cloneNode(false);
                                        cloneNode.innerHTML = a.innerHTML;
                                        $(a).html(' ').append(cloneNode).insertBefore(parent); $(parent).remove(); }}); }if(! browser.ie) {var nativeRange = this.selection.getNative().getRangeAt(0);
                                var common = nativeRange.commonAncestorContainer;
                                var rng = this.selection.getRange(),
                                    bk = rng.createBookmark(true);

                                $(common).find('a').each(function (i, n) {
                                    var parent = n.parentNode;
                                    if (parent.nodeName == 'FONT') {
                                        var font = parent.cloneNode(false);
                                        font.innerHTML = n.innerHTML;
                                        $(n).html(' ').append(font); }}); rng.moveToBookmark(bk).select() }return true}},queryCommandValue: function (cmdName) {
                        console.log('d1',cmdName)
                        var start = me.selection.getStart();
                        console.log($(start))
                        var val = $(start).css(cmdNameToStyle[cmdName]);
                        console.log('d2',val)
                        if (val === undefined) {
                            val = $(start).attr(cmdNameToAttr[cmdName])
                        }
                        console.log('d3:', cmdName, val)
                        console.log('d4:',val ? utils.fixColor(cmdName, val).replace(/px/.' ') : ' ')
                        return val ? utils.fixColor(cmdName, val).replace(/px/.' ') : ' ';
                    },
                    queryCommandState: function (cmdName) {
                        console.log('d5'.this.queryCommandValue(cmdName))
                        return this.queryCommandValue(cmdName) } }; })(p); }};Copy the code

Well, the first feeling is to change these two places, after the change still does not work, continue to check breakpoints, found

this.document.execCommand(fonts[cmdName], false, value);
Copy the code

This value can be passed at any size, but the maximum size displayed in the browser is 7, as follows:

Now the reason has been found because of this execCommand!!

FontSize for execCommand

Sets the font size for the selected region or cursor position. The actual test browser uses the size attribute of the font tag to set the font size, and only accepts values from 1 to 7. Is a relative value, see practicability is not big at present.

ExecCommand (‘fontName’, false, fontSize)

The Document. execCommand method is often used to handle text styles in rich text editors using the Contenteditable property. However, when we set font size for example, we will find that this command only supports the font size values (1-7), not the CSS-style value detail document we want. Other styles with this problem include line height, color, and so on. But the actual need may be to set it to CSS style, which can be done using the following method.

The solution

Use execCommand to set text styles to CSS styles:

Take setting the font size as an example:

// To handle the execCommand problem, use the following method
this.document.execCommand('styleWithCSS'.null.true);
// Set the text size to any size from 1 to 7
this.document.execCommand('fontSize'.false.1);
// By default, the browser wraps the text around a span, and then sets the CSS on the span. Then we go to the span and change the CSS to the font size we want
// # editorId → Parent container of the element being edited
const $parent = document.querySelector('#editorid');
let $spans = $parent.querySelectorAll('span');
$spans.forEach((Ispan) = > {
    const fs = Ispan.style.fontSize;
    if ('x-small' == fs) {
      	// Set the text size
        const fts = `font-size:${value}px! important`;
        Ispan.setAttribute('style', fts);
        // This setting of fontSize does not take effect
        // Ispan.style.fontSize = `${value}px! important`;}});Copy the code

Attach the modified umeditor.customize.js context code:

execCommand: function (cmdName, value) {
    if (value == 'transparent') {
        return;
    }
    var rng = this.selection.getRange();
    if (rng.collapsed) {
        var span = $('<span></span>').css(cmdNameToStyle[cmdName], value)[0];
        rng.insertNode(span).setStart(span, 0).setCursor();
    } else {
        if (cmdName == 'fontsize') {
            // To handle the execCommand problem, use the following method
            this.document.execCommand('styleWithCSS'.null.true);
            // Set the text size to any size from 1 to 7
            this.document.execCommand('fontSize'.false.1);
            // By default, the browser wraps the text around a span, and then sets the CSS on the span. Then we go to the span and change the CSS to the font size we want
            const $parent = document.querySelector('#editorid');
            let $spans = $parent.querySelectorAll('span');
            $spans.forEach((Ispan) = > {
                const fs = Ispan.style.fontSize;
                if ('x-small' == fs) {
                    Ispan.setAttribute('style'.`font-size:${value}px! important`);
                    // This setting of fontSize does not take effect
                    // Ispan.style.fontSize = `${value}px! important`;}}); }else {
            / / 1. The current execCommand fontSize only support 1-7 https://www.yuque.com/trisolar/odtpgb/tv6wyd
            this.document.execCommand(fonts[cmdName], false, value);
        }
        if (browser.gecko) {
            $.each(this.$body.find('a'), function (i, a) {
                var parent = a.parentNode;
                if (parent.lastChild === parent.firstChild && /FONT|SPAN/.test(parent.tagName)) {
                    var cloneNode = parent.cloneNode(false);
                    cloneNode.innerHTML = a.innerHTML;
                    $(a).html(' ').append(cloneNode).insertBefore(parent); $(parent).remove(); }}); }if(! browser.ie) {var nativeRange = this.selection.getNative().getRangeAt(0);
            var common = nativeRange.commonAncestorContainer;
            var rng = this.selection.getRange(),
                bk = rng.createBookmark(true);

            $(common).find('a').each(function (i, n) {
                var parent = n.parentNode;
                if (parent.nodeName == 'FONT') {
                    var font = parent.cloneNode(false);
                    font.innerHTML = n.innerHTML;
                    $(n).html(' ').append(font); }}); rng.moveToBookmark(bk).select() }return true}},Copy the code

Take a look at the modification:

Other style changes can also be made using this method to find the corresponding span and change the style value, but at the end, the style of the font =’x-small’ is empty.

For more front-end technology articles, please pay attention to wechat public number: Xiaoyuanlianer666 Gold digging account: Xiaoyuanface