如何在自适应文本框中输入时停止窗口跳动

7

我正在使用接受的答案来构建一个文本框,当文字溢出时能够在垂直方向上自动扩展。问题链接为:这里

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

直到文本框的大小超过浏览器窗口时,它才能正常工作。此时,每次按键时,窗口顶部都会跳转到文本区域的顶部。你可以帮我理解为什么会这样,并告诉我如何修复它吗?
理想情况下,修复方法是完全不让页面移动。但如果更容易将页面底部与文本区域底部绑定在一起,那也可以。
我在Firefox 21.0和Chrome 28.0中遇到了这个问题:http://jsfiddle.net/CbqFv/

我在火狐浏览器中没有遇到这个问题,但在谷歌浏览器中看到了它。 - Patrick Moore
6个回答

14

在调整文本区域大小之前,保存 scrollLeftscrollTop 值,然后在调整完成后将它们恢复:

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
@losnir,你知道除了在可滚动的div上下文中之外,还有什么方法可以实现这个吗?我有一个textarea位于quickview div内部,因此它的位置不是窗口的位置,而是div的位置。 - NeedsHelp

1

我的回答基于textarea高度随内容增加而增加,但内容减少时不会减小的已接受答案中的示例。

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

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

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

我获取初始高度的原因是为了避免文本区域跳转到1或任何其他静态数字来触发scrollHeight的值。(据我所知,这就是屏幕滚动改变的原因,因为文本框被设置为该静态高度(通常为1),然后扩展)我将20添加到scrollHeight的原因是有时scrollHeight偏差为1或2像素,文本看起来拥挤在底部。所以我个人会添加20。

1
当文本区域位于可滚动的 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);
    }
};

或者你可以像我一样使用被接受的答案:https://dev59.com/snXYa4cB1Zd3GeqP9r2q#66070525 - Crimin4L
我不得不将 this 更改为 this.parentNode,否则它会将 textarea 作为 scrollParent,导致什么也没有发生。 - Expenzor

1
你需要滚动页面到:滚动条位置 +(调整大小后的文本区域高度 - 调整大小前的文本区域高度)
这是代码:
    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的方法来实现(参考这个答案):

    $('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();

1
你知道在不使用可滚动的div上下文中实现这个的方法吗?我有一个textarea位于quickview div内,所以它的定位不是窗口而是div。 - NeedsHelp

1

我发现这是目前为止最好的解决方案,将Adam Beres-Deak和@losnir的代码结合起来,得出了以下结果。

到目前为止,这是其中最有用的一个:

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();
<textarea data-adaptheight style="padding: 7px;width: 100%;display: block;" rows="1" cols="40" placeholder="Your input">
</textarea>


1
根据losnir的回答,在Firefox桌面版(88)中,window.pageXOffset返回一个非整数值,但是window.scrollTo()将其四舍五入为整数值。因此,如果初始的window.pageXOffset值有0.5或更多的小数部分,则在每次文本更改时,文本区域框会向下移动一小部分。这可能会在其他浏览器中发生。解决方法是将滚动变量声明为四舍五入的,然后在第一次文本更改时会有一些微小的偏移,之后所有的值都将是整数,不会再发生进一步的偏移。代码如下:
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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接