Search code examples
ckeditor

ckeditor balloonpanel not staying attached to an element when scrolling


UPDATE balloon panels are staying attached in below code. We still have an issue where when we close a balloon panel, and then scroll afterward, the balloon panel that was just closed reappears. Here’s the updated code.

HERE WAS THE ORIGINAL QUESTION I am trying to get the ckeditor balloonpanel to stay attached to the element it was initially attached to; currently, when I scroll in the editor, the balloonpanels do not stay in place. The problem is that the balloonpanels shift when the user scrolls in the editor -- they do not remain attached to their initial element they were attached to when I scroll in the editor. Here is the code for the ckeditor plugin. It creates a balloonpanel in a for loop on return of an web service Ajax call and stores the panel in a global array called panels :


( function() {
    var arr = new Array();
    var panels = [];

    var saveCmd = {
        readOnly: 1,
        modes: { wysiwyg: 1,source: 1 },

        exec: function( editor ) {
            if ( editor.fire( 'grade_level_score' ) ) {
                var $form = editor.element.$.form;

                /**
                 * Destroys the balloon panel by removing it from DOM and purging
                 * all associated event listeners.
                 */
                    // https://github.com/ckeditor/ckeditor-dev/blob/64749bb245d1e91f6a4ac4e97c9648ec47acda91/plugins/balloonpanel/plugin.js#L743-L745
                var panel;
                while ( ( panel = panels.pop() ) ) {
                    panel.destroy();
                }

                arr = []; // clear the array of user-editable areas
                panels = []; // clear the array of panels

                // https://stackoverflow.com/a/48022658
                var ele = $(editor.editable().$);
                var elementOfClass;
                var i = 1;

                // class "ice-ice-editable" is in a span
                $('span',ele).each(function(){

                    // https://stackoverflow.com/a/35866999
                    var iceIceEditableClass = "ice-ice-editable";
                    var hasClassIceIceEditable = $(this).hasClass(iceIceEditableClass);

                    if( hasClassIceIceEditable ) {
                        console.log($(this).text());
                        console.log($(this).attr('class'));
                        console.log($(this).attr('id'));

                        var userEditable = "user-editable-" + i;

                        // If the specified attribute already exists, only the value is set/changed.
                        this.setAttribute("id","user-editable-" + i);

                        var record1 = { id : userEditable , userEditableArea : $(this).text() };
                        arr.push(record1);

                        i++;
                    }
                });

                var gradeLevelObject = new Object();
                gradeLevelObject.textAreas = arr;


                // var responseGradeLevelScoreWS = gradeLevelScore(gradeLevelObject);

                // BEGIN for testing
               var result = '{"textAreas":[{"id":"user-editable-1","userEditableArea":"[Insert information specific to what is being addressed (a brief description of request(s) and/or concern(s). Specific training resource document for letter writing assistance will be referenced here.]  ","score":22.24,"readingGrade":7,"issues":["asdf","zxcv"]},{"id":"user-editable-2","userEditableArea":"[Insert information specific to what is being addressed (a brief description of request(s) and/or concern(s). Specific training resource document for letter writing assistance will be referenced here.]  ","score":22.24,"readingGrade":0,"issues":[]},{"id":"user-editable-3","userEditableArea":"[Insert information specific to what is being addressed (a brief description of request(s) and/or concern(s). Specific training resource document for letter writing assistance will be referenced here.]  ","score":22.24,"readingGrade":0,"issues":[]},{"id":"user-editable-4","userEditableArea":"[Insert information specific to what is being addressed (a brief description of request(s) and/or concern(s). Specific training resource document for letter writing assistance will be referenced here.]  ","score":22.24,"readingGrade":0,"issues":[]}]}';
               var responseGradeLevelScoreWS = JSON.parse(result);
                // END for testing

                console.log(responseGradeLevelScoreWS);

                var i;
                for (i = 0; i < responseGradeLevelScoreWS.textAreas.length; i++){

                    if ( responseGradeLevelScoreWS.textAreas[i].readingGrade > 6 ) {
                        var j;
                        var issues = '';
                        for (j = 0; j < responseGradeLevelScoreWS.textAreas[i].issues.length; j++) {
                            issues += '<p>' + responseGradeLevelScoreWS.textAreas[i].issues[j]  + '</p>';
                        }

                        panel = new CKEDITOR.ui.balloonPanel( editor, {
                            title: 'Grade: ' + responseGradeLevelScoreWS.textAreas[i].readingGrade + '. Score: ' + responseGradeLevelScoreWS.textAreas[i].score,
                            content: ( (typeof issues === 'undefined' || issues == null) ? 'There are no suggestions in order to descrease the grade level score' : issues ),
                            width: 500,
                            height: 120
                        });

                        var element = editor.document.getById(responseGradeLevelScoreWS.textAreas[i].id);

                        panel.attach( element );

                        panel.registerFocusable(element);
                        panels.push( panel );
                        issues = '';
                    }
                }

                // We'll use throttling for scroll listener to reduce performance impact.
                var scrollListener = CKEDITOR.tools.eventsBuffer( 100, function() {
                    for (i = 0; i < panels.length; i++) {

                        panels[i].attach( editor.document.getById( responseGradeLevelScoreWS.textAreas[i].id ), {
                            focusElement: false,
                            show: false
                        } );

                    }
                } );
                editor.window.on( 'scroll', scrollListener.input );

                if ( $form ) {
                    try {

                        //$form.submit();
                    } catch ( e ) {
                        // If there's a button named "submit" then the form.submit
                        // function is masked and can't be called in IE/FF, so we
                        // call the click() method of that button.
                        if ( $form.submit.click )
                            $form.submit.click();
                    }
                }
            }
        }
    };

    var pluginName = 'grade_level_score';

    // Register a plugin named "save".
    CKEDITOR.plugins.add( pluginName, {
        // jscs:disable maximumLineLength
        lang: 'en,en-au,en-ca,en-gb,es,es-mx', // %REMOVE_LINE_CORE%
        // jscs:enable maximumLineLength
        icons: 'grade_level_score', // %REMOVE_LINE_CORE%
        hidpi: true, // %REMOVE_LINE_CORE%
        init: function( editor ) {
            // Save plugin is for replace mode only.
            if ( editor.elementMode != CKEDITOR.ELEMENT_MODE_REPLACE )
                return;

            var command = editor.addCommand( pluginName, saveCmd );
            command.startDisabled = !( editor.element.$.form );

            editor.ui.addButton && editor.ui.addButton( 'Grade_Level_Score', {
                //label: editor.lang.save.toolbar,
                label: "Grade Level Score",
                command: pluginName,
                toolbar: 'custom,100'
            } );
        }
    } );
} )();

Solution

  • Only Balloon Toolbar has built-in functionality for automatic reposition on scroll. Balloon Panel itself is a static element. However, it can be easily achieved by attaching scroll listener and repositioning visible panels on scroll:

    // We'll use throttling for scroll listener to reduce performance impact.
    var scrollListener = CKEDITOR.tools.eventsBuffer( 100, function() {
      for (i = 0; i < panels.length; i++) {
        panels[i].attach( editor.document.getById(ids[i]), {
          focusElement: false,
          show: false
        } );
      }
    } );
    editor.window.on( 'scroll', scrollListener.input );
    

    See this codepen for the full code (reusing some parts of your original code).