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

Expanding textarea to fit height gets slow with JS. Looking into div and contenteditable instead

我使用 Autosize 使文本区域适合文本的高度。它工作正常,但在数百个文本区域(这可能是预期的)上变得非常慢。

我研究过将 divcontenteditable 一起使用,经过大量尝试后,我找到了一个可以满足我要求的解决方案,但它似乎有点老套,尤其是在 Firefox 中用奇怪的双换行处理。我能做些什么来进一步完善它或以更简洁的方式实现上述技巧吗?

在 Mac 上的 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>

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

// 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>