如何处理文本区域中的 <tab>?

Posted

技术标签:

【中文标题】如何处理文本区域中的 <tab>?【英文标题】:How to handle <tab> in textarea? 【发布时间】:2011-09-02 16:40:31 【问题描述】:

我想要一个文本区域来处理按下 tab 键的情况。

在默认情况下,如果您按下 tab 键,则焦点离开文本区域。但是当用户想要在 textarea 中键入 tab 键的情况下呢?

我可以捕捉这个事件并将焦点返回到 textarea 并将标签添加到 当前 光标位置吗?

【问题讨论】:

Use tab to indent in textarea的可能重复 我查看了一些现有的答案,但它们在某些方面都存在不足(无法撤消、性能不佳、取消缩进后选择不正确)。我最近写了一个可以正确处理这个问题的小模块,叫做indent-textarea 【参考方案1】:

您可以:http://jsfiddle.net/sdDVf/8/。


$("textarea").keydown(function(e) 
    if(e.keyCode === 9)  // tab was pressed
        // get caret position/selection
        var start = this.selectionStart;
        var end = this.selectionEnd;

        var $this = $(this);
        var value = $this.val();

        // set textarea value to: text before caret + tab + text after caret
        $this.val(value.substring(0, start)
                    + "\t"
                    + value.substring(end));

        // put caret at right position again (add one for the tab)
        this.selectionStart = this.selectionEnd = start + 1;

        // prevent the focus lose
        e.preventDefault();
    
);

【讨论】:

@Amine: 阻止浏览器的默认选项卡功能。我现在看到没有必要。我将对此进行搜索。编辑:return false 似乎包括preventDefault:***.com/questions/1357118/…。 也实现了选择覆盖。 @sergzach:我的解决方案在 IE8 中根本不起作用,因为它不支持 selectionStart/selectionEnd。您必须创建文本范围,但恐怕我仍然不明白它是如何工作的...... 这会阻止浏览器的撤消功能 (Ctrl+z) 不确定如何/为什么错过了,在最后一个子字符串调用 start 应该作为第一个参数给出:+ value.substring(start, end)); -- 否则在选择文本时使用 tab 键会擦除文本。【参考方案2】:

这是 pimvdb 答案的修改版本,不需要 JQuery:

document.querySelector("textarea").addEventListener('keydown',function(e) 
    if(e.keyCode === 9)  // tab was pressed
        // get caret position/selection
        var start = this.selectionStart;
        var end = this.selectionEnd;

        var target = e.target;
        var value = target.value;

        // set textarea value to: text before caret + tab + text after caret
        target.value = value.substring(0, start)
                    + "\t"
                    + value.substring(end);

        // put caret at right position again (add one for the tab)
        this.selectionStart = this.selectionEnd = start + 1;

        // prevent the focus lose
        e.preventDefault();
    
,false);

我在 Firefox 21.0 和 Chrome 27 中对其进行了测试。不知道它是否适用于其他任何地方。

【讨论】:

要将其应用于所有 textarea 使用 querySelectorAll,枚举返回的列表,并将事件侦听器添加到每个元素。 要使这种方法发挥作用,您需要将 eventListener 附加到 individual DOM 元素。为此:1) 获取所有元素document.querySelectorAll("textarea") 2) 通过数组循环以将事件侦听器附加到每个元素。然后它应该工作 将撤消功能作为接受的答案。【参考方案3】:

天哪,以前的所有答案都未能提供通常体面的(即对于程序员而言)选项卡控件。

也就是说,点击 TAB 选择行将缩进这些行,而 SHIFTTAB 将取消缩进。

_edited(2016 年 11 月):keyCode 替换为 charCode || keyCode,根据KeyboardEvent.charCode - Web APIs | MDN

(function($) 
  $.fn.enableSmartTab = function() 
    var $this;
    $this = $(this);
    $this.keydown(function(e) 
      var after, before, end, lastNewLine, changeLength, re, replace, selection, start, val;
      if ((e.charCode === 9 || e.keyCode === 9) && !e.altKey && !e.ctrlKey && !e.metaKey) 
        e.preventDefault();
        start = this.selectionStart;
        end = this.selectionEnd;
        val = $this.val();
        before = val.substring(0, start);
        after = val.substring(end);
        replace = true;
        if (start !== end) 
          selection = val.substring(start, end);
          if (~selection.indexOf('\n')) 
            replace = false;
            changeLength = 0;
            lastNewLine = before.lastIndexOf('\n');
            if (!~lastNewLine) 
              selection = before + selection;
              changeLength = before.length;
              before = '';
             else 
              selection = before.substring(lastNewLine) + selection;
              changeLength = before.length - lastNewLine;
              before = before.substring(0, lastNewLine);
            
            if (e.shiftKey) 
              re = /(\n|^)(\t|[ ]1,8)/g;
              if (selection.match(re)) 
                start--;
                changeLength--;
              
              selection = selection.replace(re, '$1');
             else 
              selection = selection.replace(/(\n|^)/g, '$1\t');
              start++;
              changeLength++;
            
            $this.val(before + selection + after);
            this.selectionStart = start;
            this.selectionEnd = start + selection.length - changeLength;
          
        
        if (replace && !e.shiftKey) 
          $this.val(before + '\t' + after);
          this.selectionStart = this.selectionEnd = start + 1;
        
      
    );
  ;
)(jQuery);

$(function() 
  $("textarea").enableSmartTab();
)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea rows="10" cols="80">
/* Just some code to edit with our new superTab */
(function($) 
    $.fn.enableSmartTab = function() 
        $this = $(this);
        $this.keydown(function(e) 
            if ((e.charCode === 9 || e.keyCode === 9) && !e.metaKey && !e.ctrlKey && !e.altKey) 
                e.preventDefault();
            
        
    

</textarea>

【讨论】:

我刚刚写了my code 来实现这个功能。 在不能使用 jQuery 的情况下,考虑使用我的代码。 @K._ 不错的代码,但我建议通过 babel 或 google 闭包运行它以删除 ES6 组件。 ES6 很棒,但是没有使用 ES6 兼容浏览器的用户太多了。 event.key 在浏览器中也不是(IIRC)一致的,键在不同的浏览器中可能被称为不同的东西,Safari 根本不支持该属性。这是人们使用 jQuery 的other 原因,虽然经常被遗忘,但它是为了保证跨浏览器的兼容性。我不是在批评你的努力,我认为你做得很好,但它必须是跨浏览器的 @K._ 如果你看一下我写的代码,里面实际上并没有太多的 jQuery。除去监听器添加,jQuery 唯一实际使用的是$.fn.val。处理程序本身已经使用原生事件属性。 ~和!~是什么意思? @1.21gigawatts 它是按位非运算符,这里有一个很好的解释为什么使用它here【参考方案4】:

在 Vanilla(默认)JS 中,这将是:

var textareas = document.getElementsByTagName('textarea');

if ( textareas ) 
  for ( var i = 0; i < textareas.length; i++ ) 
textareas[i].addEventListener( 'keydown', function ( e ) 
  if ( e.which != 9 ) return;

  var start             = this.selectionStart;
  var end                 = this.selectionEnd;

  this.value            = this.value.substr( 0, start ) + "\t" + this.value.substr( end );
  this.selectionStart = this.selectionEnd = start + 1;

  e.preventDefault();
  return false;
);
  
textarea 
   border: 1px solid #cfcfcf;
   width: 100%;
   margin-left: 0px;
   top: 0px;
   bottom: 0px;
   position: absolute;
<textarea>
var x = 10;
var y = 10;
</textarea>

【讨论】:

谢谢,伙计。人们没有意识到并不是每个人都在使用 jQuery。 @Mark 你能减小标签大小吗? 这样更好吗?【参考方案5】:

在(多个)textarea 元素内启用制表符

纠正@alexwells 的答案并启用现场演示

var textAreaArray = document.querySelectorAll("textarea");
    for (var i = textAreaArray.length-1; i >=0;i--)
        textAreaArray[i].addEventListener('keydown',function(e) 
            if(e.keyCode === 9)  // tab was pressed
                // get caret position/selection
                var start = this.selectionStart;
                var end = this.selectionEnd;

                var target = e.target;
                var value = target.value;

                // set textarea value to: text before caret + tab + text after caret
                target.value = value.substring(0, start)
                            + "\t"
                            + value.substring(end);

                // put caret at right position again (add one for the tab)
                this.selectionStart = this.selectionEnd = start + 1;

                // prevent the focus lose
                e.preventDefault();
            
        ,false);
    
<textarea rows="10" cols="80"></textarea>
   <textarea rows="10" cols="80"></textarea>

【讨论】:

使用原生 javascript 在 textarea 元素中启用制表符【参考方案6】:

在谷歌搜索时发现了这个。我做了一个很短的,也可以缩进和反向缩进选择的文本:

    jQ(document).on('keydown', 'textarea', function(e) 
        if (e.keyCode !== 9) return;
        var Z;
        var S = this.selectionStart;
        var E = Z = this.selectionEnd;
        var A = this.value.slice(S, E);
        A = A.split('\n');
        if (!e.shiftKey)
            for (var x in A) 
                A[x] = '\t' + A[x];
                Z++;
            
        else
            for (var x in A) 
                if (A[x][0] == '\t')
                    A[x] = A[x].substr(1);
                Z--;
            
        A = A.join('\n');
        this.value = this.value.slice(0, S) + A + this.value.slice(E);
        this.selectionStart = S != E ? S : Z;;
        this.selectionEnd = Z;
        e.preventDefault();
    );

【讨论】:

以上是关于如何处理文本区域中的 <tab>?的主要内容,如果未能解决你的问题,请参考以下文章

使用Javascript在文本区域中的光标处插入文本

如何以角度突出显示文本区域中的文本?

如何在引导程序 2.3.2 中对齐文本区域中的文本

下划线文本区域中的部分文本[重复]

识别不同文本区域中的光标位置并在列表框中选择项目时插入文本

如何查找文本区域中的文本是不是换行为多行?