代码之家  ›  专栏  ›  技术社区  ›  SeaBass

用JS扩展文本区域以适应高度会变得很慢。改为查看div和contenteditable

  •  -2
  • SeaBass  · 技术社区  · 7 年前

    我在用 Autosize 使文本区域适合文本的高度。它正在工作,但在几百个文本区域(这可能是意料之中的)上速度变得非常慢。

    我研究了使用 div 具有 contenteditable 相反,经过多次尝试,我找到了一个可以随心所欲的解决方案,但它看起来有点老套,特别是在Firefox中使用了奇怪的双换行处理。有什么我可以做的进一步完善或实现上述黑客以更整洁的方式?

    在Firefox上,如果你在中间的某个地方编辑文本,你仍然可以在回车上得到2个分线。有什么解决办法吗?

    // Paste fix for contenteditable
    $('[contenteditable]').on('paste', function (e) {
        e.preventDefault();
    
        if (window.clipboardData)
        {
            content = window.clipboardData.getData('Text');        
            if (window.getSelection)
            {
                var selObj = window.getSelection();
                var selRange = selObj.getRangeAt(0);
                selRange.deleteContents();                
                selRange.insertNode(document.createTextNode(content));
            }
        }
        else if (e.originalEvent.clipboardData)
        {
            content = (e.originalEvent || e).clipboardData.getData('text/plain');
            document.execCommand('insertText', false, content);
        }        
    });
    
    // Set Current Value on Focus
    $(document).on('focus', '.edit-area', function() {
        var $self = $(this);
        if( $self.is("div") )
        {
            cur_val = $self[0].innerText.trim();
        }
        else
        {
            cur_val = $self.val();
        }
    });
    
    // Blur on enter and fix line-breaks especially for FF
    $(document).on('keydown', '.edit-area', function(e) {
        var $self = $(this);
        var esc = e.keyCode == 27;
        var nl = e.keyCode == 13;
    
        if (esc)
        {
            // Restore if ESC
            if( $self.is("div") )
            {
                $self.text(cur_val);
            }
            else
            {
                $self.val(cur_val);
            }
            $self.blur();
        }
        else if (nl)
        {
            if( $self.is("div") )
            {
                /* Tried this instead of the document.execCommand below but it leaves a space in there which I'd love to avoid
                e.preventDefault(); //Prevent default browser behavior
                if (window.getSelection) {
                    var selection = window.getSelection(),
                    range = selection.getRangeAt(0),
                    br = document.createElement("br"),
                    textNode = document.createTextNode($("<div>&nbsp;</div>").text()); //Passing " " directly will not end up being shown correctly
                    range.deleteContents();//required or not?
                    range.insertNode(br);
                    range.collapse(false);
                    range.insertNode(textNode);
                    range.selectNodeContents(textNode);
    
                    selection.removeAllRanges();
                    selection.addRange(range);
                    return false;
                }*/
                /* Tried this but it still gives an extra br in FF
                document.execCommand('insertHTML', false, '<br><br>'); // fix for line-breaks
                return false;*/
                // This Seems to be the best solution so far...
                if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
                   var sel, node, offset, text, textBefore, textAfter, range;
    
                   sel = window.getSelection();
    
                   // the node that contains the caret
                   node = sel.anchorNode;
    
                   // if ENTER was pressed while the caret was inside the input field
    
                   // prevent the browsers from inserting <div>, <p>, or <br> on their own
                   e.preventDefault();
    
                   // the caret position inside the node
                   offset = sel.anchorOffset;        
    
                   // insert a '\n' character at that position
                   text = node.textContent;
                   textBefore = text.slice( 0, offset );
                   textAfter = text.slice( offset ) || ' ';
                   node.textContent = textBefore + '\n' + textAfter;
    
                   // position the caret after that new-line character
                   range = document.createRange();
                   range.setStart( node, offset + 1 );
                   range.setEnd( node, offset + 1 );
    
                   // update the selection
                   sel.removeAllRanges();
                   sel.addRange( range );
                }
            }
        }
    });
    
    // Save data on blur
    $(document).on('blur', '.edit-area', function() {
        var $self = $(this);
        if( $self.is("div") )
        {
            var value = $self[0].innerText.trim();
            $self.text(value);
        }
        else
        {
            if( $self.val() instanceof Array )
            {
                var value = $self.val();
            }
            else
            {
                var value = trim($self.val());
                $self.val(value);
            }
        }
        if( String(value) == String(cur_val) ) 
        {
            return false; // Return false if value is current value
        }
        // Save to database here...
        alert("Save this to database:\n\n"+value);
    });
    div.edit-area[contenteditable] {
      outline: none;
      white-space: pre-wrap;
    }
    div.edit-area[contenteditable]:empty:before {
      content: '-';
    }
    div.edit-area[contenteditable]:focus:before {
      color: transparent;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div contenteditable="true" class="edit-area">Editable text with
    line-breaks
    preserved, enter hack, paste fix, trimmed result and no html</div>
    1 回复  |  直到 7 年前
        1
  •  0
  •   SeaBass    7 年前

    在Safari、Chrome和FF for Mac中工作和测试。

    // Paste fix for contenteditable
    $('[contenteditable]').on('paste', function (e) {
        e.preventDefault();
    
        if (window.clipboardData)
        {
            content = window.clipboardData.getData('Text');        
            if (window.getSelection)
            {
                var selObj = window.getSelection();
                var selRange = selObj.getRangeAt(0);
                selRange.deleteContents();                
                selRange.insertNode(document.createTextNode(content));
            }
        }
        else if (e.originalEvent.clipboardData)
        {
            content = (e.originalEvent || e).clipboardData.getData('text/plain');
            document.execCommand('insertText', false, content);
        }        
    });
    
    // Set Current Value on Focus
    $(document).on('focus', '.edit-area', function() {
        var $self = $(this);
        if( $self.is("div") )
        {
            cur_val = $self[0].innerText.trim();
        }
        else
        {
            cur_val = $self.val();
        }
    });
    
    // Blur on enter and fix line-breaks especially for FF
    $(document).on('keydown', '.edit-area', function(e) {
        var $self = $(this);
        var esc = e.keyCode == 27;
        var nl = e.keyCode == 13;
    
        if (esc)
        {
            // Restore if ESC
            if( $self.is("div") )
            {
                $self.text(cur_val);
            }
            else
            {
                $self.val(cur_val);
            }
            $self.blur();
        }
        else if (nl)
        {
            if( $self.is("div") )
            {
                if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
                   var sel, node, offset, text, textBefore, textAfter, range;
    
                   sel = window.getSelection();
    
                   // the node that contains the caret
                   node = sel.anchorNode;
    
                   // if ENTER was pressed while the caret was inside the input field
    
                   // prevent the browsers from inserting <div>, <p>, or <br> on their own
                   e.preventDefault();
    
                   // the caret position inside the node
                   offset = sel.anchorOffset;        
    
                   // insert a '\n' character at that position
                   text = node.textContent;
                   textBefore = text.slice( 0, offset );
                   textAfter = text.slice( offset ) || ' ';
                   node.textContent = textBefore + '\n' + textAfter;
    
                   // position the caret after that new-line character
                   range = document.createRange();
                   range.setStart( node, offset + 1 );
                   range.setEnd( node, offset + 1 );
    
                   // update the selection
                   sel.removeAllRanges();
                   sel.addRange( range );
                }
            }
        }
    });
    
    // Save data on blur
    $(document).on('blur', '.edit-area', function() {
        var $self = $(this);
        if( $self.is("div") )
        {
            var value = $self[0].innerText.trim();
            $self.text(value);
        }
        else
        {
            if( $self.val() instanceof Array )
            {
                var value = $self.val();
            }
            else
            {
                var value = trim($self.val());
                $self.val(value);
            }
        }
        if( String(value) == String(cur_val) ) 
        {
            return false; // Return false if value is current value
        }
        // Save to database here...
        alert("Save this to database:\n\n"+value);
    });
    div.edit-area[contenteditable] {
      outline: none;
      white-space: pre-wrap;
    }
    div.edit-area[contenteditable]:empty:before {
      content: '-';
    }
    div.edit-area[contenteditable]:focus:before {
      color: transparent;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div contenteditable="true" class="edit-area">Editable text with
    line-breaks
    preserved, enter hack, paste fix, trimmed result and no html</div>