Search code examples
javascriptjqueryhtmliframewysiwyg

Can't use the `on` method on an array of classes


Problem:

I am trying to create a WYSIWYG editor that is modular and reusable for my blog... unfortunately I am having a hard time making it work with more than one text field.

How it works:

The editor enables document.designMode = 'on' on an Iframe.

First I initialize the object:

init: function(){
    this.cacheDOM();
    this.renderEditor();
    this.enableDesignMode();
    this.bindEvents();
},

Then cache the DOM

cacheDOM: function(){
    this.$editors = $('.editor-toolbar');
    this.$iframes = $('.editor-iframe');
},

Then I render the toolbar with all my editor tools

renderEditor: function(){
    var html = '<div class="text-editor">' +
        '<div class="btn-group">' +
            '<button class="e_bold      btn btn-sm btn-secondary" type="button"><i class="fa fa-bold"></i></button>' +
            '<button class="e_italic    btn btn-sm btn-secondary" type="button"><i class="fa fa-italic"></i></button>' +
            '<button class="e_underline btn btn-sm btn-secondary" type="button"><i class="fa fa-underline"></i></button>' +
        '</div>' +
        '<div class="btn-group">' +
            '<button class="e_align_left   btn btn-sm btn-secondary" type="button"><i class="fa fa-align-left"></i></button>' +
            '<button class="e_align-center btn btn-sm btn-secondary" type="button"><i class="fa fa-align-center"></i></button>' +
            '<button class="e_align-right  btn btn-sm btn-secondary" type="button"><i class="fa fa-align-right"></i></button>' +
        '</div>' +
        '<div class="btn-group">' +
            '<button class="e_header_2  btn btn-sm btn-secondary" type="button" value="h2">h2</button>' +
            '<button class="e_header_3  btn btn-sm btn-secondary" type="button" value="h3">h3</button>' +
            '<button class="e_header_4  btn btn-sm btn-secondary" type="button" value="h4">h4</button>' +
            '<button class="e_paragraph btn btn-sm btn-secondary" type="button" value="p">p</button>' +
        '</div>' +
        '<div class="btn-group">' +
            '<button class="e_unordered_list  btn btn-sm btn-secondary" type="button"><i class="fa fa-list-ul"></i></button>' +
            '<button class="e_ordered_list    btn btn-sm btn-secondary" type="button"><i class="fa fa-list-ol"></i></button>' +
            '<button class="e_table_separator btn btn-sm btn-secondary" type="button">1 | 2</button>' +
        '</div>' +
        '<div class="btn-group">' +
            '<button class="e_unordered_list  btn btn-sm btn-outline-success" type="button"><i class="fa fa-tag"></i></button>' +
            '<button class="e_ordered_list    btn btn-sm btn-outline-info" type="button"><i class="fa fa-tag"></i></button>' +
            '<button class="e_table_separator btn btn-sm btn-outline-secondary" type="button"><i class="fa fa-tag"></i></button>' +
        '</div>' +
    '</div>';
    this.$editors.html(html);
},

Next I enable the designMode. I want to have as many separate editors as I want on a page so I loop through each Iframe and enable design mode on each of them.

enableDesignMode: function(){

    for (i = 0; i < this.$iframes.length; i++) {
        this.$iframes[i].contentDocument.designMode = 'on';
    }
},

Now comes the difficulty. I am trying to bind click events on buttons in this.$editors[0] to this.$iframes[0] so that each toolbar is attached to the correct editor iframe.

bindEvents: function(){
    for (i = 0; i < this.$iframes.length; i++) {
        this.$editors[0].on('click', 'e_bold', function(){
            this.$iframes[i].contentDocument.execCommand('bold', false, null);
        });
    }
},

Problem:

I am getting this error and I don't understand why:

TypeError: this.$editors[0].on is not a function

this.$editors is an array of classes... so surely the .on function should apply to this.$editors[i]?

Here is the fiddle: https://jsfiddle.net/7q30tuxo/1/


Solution

  • You are mistaken, this.$editors is a jQuery object, not "an array of classes". When you do this.$editors[0], you are getting an actual HTML element.

    on is a function on jQuery objects, not DOM elements. Wrap your this.$editors[n] with jQuery: $(this.$editors[0])

    EDIT: As Kevin mentioned in the comment below, you're not selecting more than one element for use in this.$editors, so you don't need to unwrap it: this.$editors.on('click', function(){})

    To be clear, you don't generally need to unwrap even if you have selected more than one element. Only if you need to treat the selected elements differently do they need to be unwrapped. That is a smell, however, and I'd recommend refactoring your selectors so that they select only the elements you intend to operate on as one.