如何在自动调整textarea中键入时停止窗口跳转

时间:2013-08-15 22:09:26

标签: javascript textarea autoresize

我正在使用this question的接受答案来构建一个文本区域,当文本溢出时,它会垂直扩展:

<!DOCTYPE html>
<html>
<head>
<title>autoresizing textarea</title>
<style type="text/css">
textarea {
 border: 0 none white;
 overflow: hidden;
 padding: 0;
 outline: none;
 background-color: #D0D0D0;
 resize: none;
}
</style>
<script type="text/javascript">
var observe;
if (window.attachEvent) {
 observe = function (element, event, handler) {
  element.attachEvent('on'+event, handler);
 };
}
else {
 observe = function (element, event, handler) {
  element.addEventListener(event, handler, false);
 };
}
function init () {
 var text = document.getElementById('text');
 function resize () {
  text.style.height = 'auto';
  text.style.height = text.scrollHeight+'px';
 }
 /* 0-timeout to get the already changed text */
 function delayedResize () {
  window.setTimeout(resize, 0);
 }
 observe(text, 'change',  resize);
 observe(text, 'cut',     delayedResize);
 observe(text, 'paste',   delayedResize);
 observe(text, 'drop',    delayedResize);
 observe(text, 'keydown', delayedResize);

 text.focus();
 text.select();
 resize();
}
</script>
</head>
<body onload="init();">
<textarea rows="1" style="height:1em;" id="text"></textarea>
</body>
</html>

直到textarea的大小比浏览器窗口长,它才能正常工作。此时,每按一次键,窗口顶部就会跳到文本区域的顶部。你能帮我理解为什么以及如何解决它吗?

理想的解决方法是保持页面不移动。但是,如果将页面底部绑在textarea的底部更容易,那也可以。

我在Firefox 21.0和Chrome 28.0中遇到此问题:http://jsfiddle.net/CbqFv/

6 个答案:

答案 0 :(得分:8)

保存scrollLeftscrollTop值,然后在调整textarea大小后恢复它们:

function resize () {
   var scrollLeft = window.pageXOffset ||
   (document.documentElement || document.body.parentNode || document.body).scrollLeft;

   var scrollTop  = window.pageYOffset ||
   (document.documentElement || document.body.parentNode || document.body).scrollTop;

   text.style.height = "auto";
   text.style.height = text.scrollHeight + 'px';

   window.scrollTo(scrollLeft, scrollTop);
}

JSFiddle:http://jsfiddle.net/losnir/nnkeH/1

答案 1 :(得分:1)

你应该将页面滚动到:滚动条位置+(调整大小后的textarea高度 - 调整大小之前的textarea高度)

这是代码:

    function resize () {
      var scrollLeft = window.pageXOffset ||
      (document.documentElement || document.body.parentNode || document.body).scrollLeft;

      var scrollTop  = window.pageYOffset ||
      (document.documentElement || document.body.parentNode || document.body).scrollTop;

      var prevHeight = text.style.height.slice(0, -2);
      text.style.height = "auto";
      var nextHeight = text.scrollHeight;
      text.style.height = text.scrollHeight + 'px';
      window.scrollTo(scrollLeft, scrollTop + (nextHeight - prevHeight));
    }

或者您可以使用jquery(基于this answer)执行此操作:

    $('body').on('keyup', 'textarea', function (e) {
      var scrollTop  = $(document).scrollTop();
      var prevHeight = $(this).height();
      $(this).css('height', 'auto');
      var nextHeight = this.scrollHeight;
      $(this).height(nextHeight);
      $(document).scrollTop(scrollTop + (nextHeight - prevHeight));
    });
    $( 'textarea' ).keyup();

答案 2 :(得分:1)

Re losnir 的回答,在 Firefox Desktop (88) 中,window.pageXOffset 似乎返回一个非整数值,但 window.scrollTo() 将其四舍五入为整数值。因此,如果初始 window.pageXOffset 值的小数部分为 0.5 或更多,那么在每次更改文本时,textarea 框都会向下移动少量。这可能发生在其他浏览器上。解决方案是将滚动变量声明为四舍五入,然后在第一次更改文本时有一个小的移位,之后所有值无论如何都将是整数,并且不会发生进一步的移位。这给出:

function resize () {
    var scrollLeft = Math.round(window.pageXOffset ||
                (document.documentElement || document.body.parentNode || document.body).scrollLeft);
   var scrollTop  = Math.round(window.pageYOffset ||
                (document.documentElement || document.body.parentNode || document.body).scrollTop);

   text.style.height = "auto";
   text.style.height = text.scrollHeight + 'px';

   window.scrollTo(scrollLeft, scrollTop);
}

答案 3 :(得分:0)

当文本区域位于可滚动的div中时,一种可以接受的答案的方法:

function getScrollParent(node) {
    if (node == null) {
        return null;
    }

    if (node.scrollHeight > node.clientHeight) {
        return node;
    } else {
        return getScrollParent(node.parentNode);
    }
}

function resize(){
    // 'this' is the textarea
    const scrollParent = getScrollParent(this);
    const scrollTop = scrollParent ? scrollParent.scrollTop : null;
    const scrollLeft = scrollParent ? scrollParent.scrollLeft : null;

    this.style.height = "auto";
    this.style.height = this.scrollHeight + "px";

    if (scrollParent) {
        scrollParent.scrollTo(scrollLeft, scrollTop);
    }
};

答案 4 :(得分:0)

我的回答基于接受的height of textarea increases when value increased but does not reduce when value is decreased答案中的小提琴。

这适用于页面加载以及以任何方式(剪切,粘贴,键入等)更改文本的情况

$('textarea').on('keyup keydown', function () {
var $this = $(this);
var initialHeight = $this.height();

$this
    .height(initialHeight - 40)
    .height($this[0].scrollHeight + 20);}).trigger('keyup');

我抓住初始高度的原因是,textarea不必跳到1或任何其他静态数字即可触发scrollHeight的值。 (据我了解,是因为文本框设置为该静态高度(通常为1)然后展开,导致屏幕更改滚动的原因)我向scrollHeight添加20的原因是因为有时scrollHeight关闭了1或2px,并且文本看起来像堆在底部。因此,为了给它更多空间,我个人添加了20。

答案 5 :(得分:0)

发现这是迄今为止最好的解决方案,结合 Adam Beres-Deak 和 @losnir 的代码并得出以下结果。

迄今为止最有用的:

                                       URL                                              Links
0  https://stackoverflow.com/questions/ask  [#, https://stackoverflow.com, /company, #, /t...
if(window.attachEvent){
    observe = function(element, event, handler){ element.attachEvent('on'+event, handler); };
}else{
    observe = function(element, event, handler){ element.addEventListener(event, handler, false); };
}
function init(){
    var textAreas = [].slice.call(document.querySelectorAll('textarea[data-adaptheight]'));
    textAreas.forEach(function(el){
        function resize(){
            var scrollLeft = window.pageXOffset ||
                (document.documentElement || document.body.parentNode || document.body).scrollLeft;
            var scrollTop = window.pageYOffset ||
                (document.documentElement || document.body.parentNode || document.body).scrollTop;
            el.style.resize = "none";
            el.style.overflow = 'hidden';
            el.style.boxSizing = el.style.mozBoxSizing = 'border-box';
            el.style.height = "auto";
            el.style.height = el.scrollHeight + 'px';
            window.scrollTo(scrollLeft, scrollTop);
        }
        observe(el, 'input', resize);
        resize();
    });
}
init();