如何处理textarea中的<tab>?


69

我想要一个文本区域,可以处理按Tab键的情况。

在默认情况下,如果您按Tab键,则焦点将离开文本区域。但是,当用户想要在文本区域中输入tab键时会怎样呢?

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



我查看了一些现有的答案,但它们都在某种程度上有所欠缺(没有撤消,性能不佳,缩进后的错误选择)。我最近编写了一个可以正确处理此问题的小模块,称为indent-textarea
fregante

Answers:


129

您可以: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();
    }
});

2
@Amine:阻止浏览器的默认选项卡功能。我现在看到没有必要。我将对此进行搜索。编辑:return false似乎包括preventDefaultstackoverflow.com/questions/1357118/…
pimvdb 2011年

1
也实现了选择覆盖。
pimvdb

@sergzach:我的解决方案在IE8中根本不起作用,因为它不支持selectionStart/ selectionEnd。您必须创建文本范围,但恐怕我还是不明白它的工作原理……
pimvdb 2011年

2
这制动了浏览器的撤消功能(Ctrl + z)
01AutoMonkey

1
不确定如何/为什么错过它,应该在最后一个子字符串调用开始处将第一个参数指定为:+ value.substring(start, end));-否则在选择文本时使用制表键会擦除文本。
Asaf

29

这是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中对其进行了测试。不知道它是否可以在其他地方使用。


1
要将其应用于所有textarea,请使用querySelectorAll,枚举返回的列表,并将事件侦听器添加到每个元素。
Jeffrey LeCours 2014年

为了使这种方法起作用,您需要将eventListener附加到单个 DOM元素。为此:1)获取所有元素document.querySelectorAll("textarea")2)循环通过数组将事件侦听器附加到每个元素。然后它应该起作用
Amjad

破坏撤消功能作为可接受的答案。
丹尼尔(Daniel)

14

天哪,以前的所有答案都未能提供通常不错的(例如,对于程序员)选项卡控件。

也就是说,TAB选择行将使这些行SHIFTTAB缩进,并将它们取消缩进。

_edited(2016年11月):将keyCode替换为charCode || 每个KeyboardEvent.charCode的keyCode -Web API | 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>


1
我只是编写我的代码来实现此功能。在不可以使用jQuery的情况下,请考虑使用我的代码。
КонстантинВан

1
@ K._不错的代码,但是我建议通过babel或google关闭运行它,以删除ES6组件。ES6很棒,但是有太多用户没有使用与ES6兼容的浏览器。而且event.key(IIRC)在各浏览器之间也不是一致的,键在不同浏览器中可能称为不同的事物,而Safari根本不支持该属性。这是人们使用jQuery的另一个原因,尽管它经常被人们遗忘,但它是为了保证跨浏览器的兼容性。我不是在批评您的努力,我认为您做了出色的工作,但是必须跨浏览器工作
Orwellophile

@ K._如果您看一下我编写的代码,实际上那里没有很多jQuery。除了监听器之外,jQuery的唯一实际用法是$.fn.val。处理程序本身已经使用了本机事件属性。
Orwellophile '16

1
〜和!〜是什么意思?
1.21吉瓦,

1
@ 1.21gigawatts是按位而非运算符,为什么在这里
PeaBrain

5

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


1
谢啦。人们没有意识到并不是每个人都在使用jQuery。
nkkollaw

@Mark可以减小选项卡的大小吗?
萨安什·辛格

这是否更好?
标记

1

启用在(多个)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元素
Amjad

0

在搜寻google时发现了这个。我做了一个很短的文章,它也可以缩进和缩进所选的文本:

    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();
    });
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.