TinyMCE limit text on scrollerActived

三世轮回 提交于 2019-12-13 00:49:47

问题


I have a textarea in my rails application to collect content from user in a database. The rails application is further feeding that text to an XML-driven flex application.

The flex application has number of fixed sized containers which wraps the text inside (from the XML created by Rails app on-the-fly), but truncates the text if it exceeds the container's height. Problem is; there is no way to present the large text in XML, so it gets adjusted automatically in the compiled flex application. And the fact is; the web-based rails app and front-tier flex app are entirely disconnected in terms of having awareness of their internal events. (like in this case; rails app has no knowledge of the overflow event for flex internal containers and relying on font-size and character/line count doesn't work in this scenario!)

Therefore, I wrote a JS function to watch and rescue the textarea's overflow situation and while setting its attributes (viz; line-height, font-size, font-family, width, height... yada yada) matching that of the flex control. The complex form in rails did the trick to have dynamic number of such textarea's control being observed by the JS function.

Here is the Prototype code to handle the overflow event with the corresponding rescue code for cleanup:

var timeout;
document.observe('dom:loaded', attach_obr);

function attach_obr() {
    $$('.active_text').each (function(text_element){
        text_element.observe('keyup', function(e){
            check_limits(text_element.id);
        });
        text_element.observe('change', function(e){
            check_limits(text_element.id);
        });
    });
}

function check_limits(eyeD) {
    if($(eyeD).scrollHeight > $(eyeD).offsetHeight){

        // overflow occured, now the rescue code here
        timeout = window.setTimeout(function() {
            $("error_notice").hide();
        }, 4000);
        $("error_notice").show().update('There is no space left in this box, please use a new box to continue adding content');

        // truncate text till the scrollbar disappears
        while($(eyeD).scrollHeight > $(eyeD).offsetHeight){
            $(eyeD).value = $(eyeD).value.slice(0, -1);
        }
    }
    else {
        if($("error_notice").innerHTML!=""){
            $("error_notice").hide().update("");
            clearTime(timeout);
        }
    }
}

[Note: It works with a minor flaw of truncating few more characters than expected in the last line. User can retype these letters till the end of that line. I guess this is because somehow the change in width of textarea due to the appearance of scroll-bar is effecting either the scrollHeight or offsetHeight during the process & there should be something more to the loop's condition ($(eyeD).scrollHeight > $(eyeD).offsetHeight)]

The while loop makes things bit slower, but at least it is serving the purpose. WYSIWYG is achieved. (I would love to hear any suggestion from the viewers to improve that inelegant code :O )

WYSIWYG is not achieved, in terms of rich/formatted text..

Incorporating Rich Text:

Rather than expecting from user to place tags inside the area , in the next phase, I am planning to deploy tinyMCE in my app. Now, to make the above function work with tinyMCE, I have the following code:

tinyMCE.init({
    theme_advanced_buttons1 : "bold, italic, underline, strikethrough, separator, justifyleft, justifycenter, justifyright, justifyfull, separator, forecolor, backcolor",
    theme:"advanced",
    mode:"textareas",
    plugins : "safari",
    width: '360px',
    height: '198px',
    setup : function(ed) {
        ed.onChange.add(function(ed, i) {
            check_limits(ed.id);
        });
    }

});

The binding and firing of events is working alright. Unfortunately, the aim to control the text overflow is not working. Reason being;

a) ed.id is the id of my textarea not the interactive panel created by tinyMCE. So, the attributes like scrollHeight are offsetHeight are not getting changed for the hidden textarea control.

b) The value of textarea in this case also contains HTML code rather than the actual text. So, it is very implicit to tell what is the actual text without markup (which in our case is required when truncating the overflowed text).

My questions:

  1. Is there a way to get the scrollHeight and offsetHeight of the control created by tinyMCE?

  2. Is there a way to get the only-text version (without markup) of inner content of tinyMCE control? (So, when I truncate the text in check_limits function, it doesn't effect/breaks the markup/DOM created by tinyMCE for the formatted text. In other words, I would be simulating the user action of pressing backspace on tinyMCE control in the while loop.)

  3. Elegant way to do this whole exercise with & without tinyMCE?

Any suggestions are greatly appreciated!


回答1:


First you need to know that tinymce creates a contenteditable iframe to let users edit html contents; contents from that iframe get written back to the textarea onSave. The textarea gets hidden in the rtinymce intiatilization process. The editor id is equal to the textarea id.

Here some suggestions:

1. Relevant code

var frameid = editor.id+'_ifr';
var currentiframe = document.getElementById(frameid);
var offsetHeight = currentiframe .contentDocument.body.offsetHeight;
var scrollHeight = currentfr.Document.body.scrollHeight

2. code for this (using jQuery)

var plain_text = $(editor.getBody()).text();

3. The only more efficient way to handle the while loop in the "without tinymce" case will be to slice off some more characters and follow a logarithmic approach. You slice off a bigger part of the string and then get to the final value in half-part paces. Example: You slice of 20 characters, but it fits. Then you slice off 10 characters of the original string. If it does not fit you try 15 characters and so on... this is more effectife then the while approach, but more complicated to develop.

EDIT:

It seems almost impossible to get the line number from the caret position. Problem here is that you do not know where the a text line breaks. Though it is easy to find out in which paragraph the cursor is located at (tinymce uses paragraphs to wrap text nodes).

There is a way to limit insertion in tinymce based on characters (i.e. limit can be set to 100 characters), but i guess this won't work for your use case unless you use a monospace font.

Another approach could be to set the tinymce css to set the editor window to the exact same width as your flex boxes (set the widht to the iframes body element should be sufficient). In this case it sould be easier to use the scrollHeigth approach - you would only need to find out if the heigth did change after insertion of text and then you could divied the heigth with the lineheigth to egt the line number. I suggest you write an own plugin to implement this. This is not that difficult. Here is a link to a tutorial for this.



来源:https://stackoverflow.com/questions/6618781/tinymce-limit-text-on-scrolleractived

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!