关于模仿知乎富文本编译器,根据光标位置滚动问题. 如何解决?

问题描述:

  1. 背景
    我用的wangEditor富文本,富文本高度是根据输入内容高度自适应的,当输入内容很多时,浏览器会出现滚动条(body)
  2. 需求.
    现在有这样一个需求, 当富文本输入的内容超出屏幕时,屏幕滚动条需要滚到光标位置,保证光标不超出屏幕.
  3. 条件
    只有内容输入过多,超出屏幕,导致光标超出视口,再将滚动条滚到光标位置.让光标展示出来
    在内容超出视口时,滚到上边,在中间插入光标填写内容可以正常添加,滚动条正常滚动
  4. 参考
    知乎的写文章 富文本编译器.实现相同的效果
  5. 我的解决思路
    富文本有一个change事件,可以在每次输入内容时触发.
    (1) 在change事件中,获取光标所在位置. 和光标距离屏幕顶部的高度.
    (2) 判断 如果光标超出视口, 那么使滚动条的高度 等于 光标距离屏幕顶端的高度
  6. 补充
    因页面有两个编译器,可以理解为一个是输入免费的内容,一个是输入付费的内容. 所以对于光标位置,滚动条滚动的位置一定要精确. 我之前实现,滚动的位置总是过大,不够精确. 我的思路不一定正确.具体参考知乎编译器. 谢谢

img

该回答引用chatgpt:
你的思路基本上是正确的,但有一些细节需要注意。

在每次输入内容时触发 change 事件是可行的,但可能会存在一些输入内容没有触发 change 事件的情况,例如输入特殊字符时。因此建议在 keyup 事件中进行位置的计算。

获取光标位置可以使用 Range 对象,可以通过 window.getSelection() 获取当前选中的 Range 对象,再通过 getBoundingClientRect() 获取 Range 对象在屏幕中的位置。

滚动条的高度应该是相对于富文本容器的,而不是相对于整个文档的。因此需要计算出富文本容器相对于整个文档的位置,再加上光标距离容器顶部的高度,就是滚动条应该滚动的位置了。

下面是代码示例:

// 获取光标相对于富文本容器的位置
function getCaretPosition() {
  const sel = window.getSelection();
  if (sel.rangeCount > 0) {
    const range = sel.getRangeAt(0);
    const rect = range.getBoundingClientRect();
    const containerRect = editor.getBoundingClientRect();
    return rect.top - containerRect.top;
  }
  return 0;
}

// 滚动到光标位置
function scrollToCaret() {
  const scrollTop = getCaretPosition();
  editor.scrollTop = scrollTop;
}

// 在 keyup 事件中触发
editor.addEventListener('keyup', function(event) {
  scrollToCaret();
});


其中,editor 是富文本容器的 DOM 元素。注意,如果富文本容器是在一个有滚动条的元素内部,需要将该元素的滚动条也考虑在内。

引用chatGPT作答,要实现类似知乎富文本编译器的效果,你可以使用以下思路:

1.获取光标所在位置和光标距离屏幕顶部的高度:
在change事件中获取光标的位置和光标距离屏幕顶部的高度,可以使用getSelection()方法和getBoundingClientRect()方法来实现。具体实现可以参考下面的代码:

let selection = window.getSelection()
let range = selection.getRangeAt(0)
let rect = range.getBoundingClientRect()
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop
let cursorTop = rect.top + scrollTop

2.判断光标是否超出视口:
判断光标是否超出视口,可以根据光标距离屏幕顶部的高度和视口的高度进行比较。如果光标超出了视口,就需要将滚动条滚动到光标位置,可以使用window.scrollTo()方法来实现。具体实现可以参考下面的代码:

let windowHeight = window.innerHeight || document.documentElement.clientHeight
if (cursorTop > scrollTop + windowHeight || cursorTop < scrollTop) {
  window.scrollTo(0, cursorTop - windowHeight / 2)
}

3.精确滚动条的位置:
根据你的补充,你需要精确滚动条的位置。可以根据滚动条的高度和视口的高度来计算出滚动条滚动的位置。具体实现可以参考下面的代码:

let scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight
let scrollBarHeight = windowHeight / scrollHeight * windowHeight
let scrollTopPercent = (cursorTop - scrollTop) / (scrollHeight - windowHeight)
let scrollTopValue = scrollTopPercent * (scrollHeight - windowHeight - scrollBarHeight)
window.scrollTo(0, scrollTopValue)

完整代码示例:

let selection = window.getSelection()
let range = selection.getRangeAt(0)
let rect = range.getBoundingClientRect()
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop
let cursorTop = rect.top + scrollTop

let windowHeight = window.innerHeight || document.documentElement.clientHeight
if (cursorTop > scrollTop + windowHeight || cursorTop < scrollTop) {
  let scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight
  let scrollBarHeight = windowHeight / scrollHeight * windowHeight
  let scrollTopPercent = (cursorTop - scrollTop) / (scrollHeight - windowHeight)
  let scrollTopValue = scrollTopPercent * (scrollHeight - windowHeight - scrollBarHeight)
  window.scrollTo(0, scrollTopValue)
}

希望这些代码可以帮助你解决问题。

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
首先需要确定滚动条需要滚动的位置,可以通过获取光标所在的区域,在计算出光标距离屏幕顶端的高度来实现精确滚动。

可以使用window.getSelection()方法获取当前的选区信息,然后使用getRangeAt()获取光标的位置,计算出光标所在的区域,再根据具体情况计算出光标距离屏幕顶端的高度。

下面是一个实现示例:

const cursorPosition = () => {
  const selection = window.getSelection(); // 获取选区信息
  const range = selection.getRangeAt(0); // 获取光标所在的Range对象
  const rect = range.getBoundingClientRect(); // 获取光标所在的区域
  const { top } = rect;

  return top + window.pageYOffset; // 计算光标距离屏幕顶端的高度
};

在change事件中,可以使用setTimeout()方法来异步执行滚动条滚动的代码,等待富文本框高度自适应完成后再执行滚动条滚动。为了保证精确滚动,可以使用window.scrollTo()方法将滚动条跳转到指定位置。

下面是一个简单的实现示例:

const editor = document.getElementById('editor');

editor.addEventListener('change', () => {
  setTimeout(() => {
    const marginTop = parseInt(getComputedStyle(editor).marginTop, 10);
    const cursorTop = cursorPosition() - marginTop; // 计算光标距离富文本框顶部的高度

    if (cursorTop > editor.scrollHeight) {
      // 光标未显示在富文本框内,需要滚动
      window.scrollTo({
        top: cursorTop,
        behavior: 'smooth',
      });
    }
  }, 0);
});

注意:由于浏览器会对滑动进行惯性处理,所以使用window.scrollTo()方法滚动时可能会出现滚动过界的情况。可以通过设置behavior: 'smooth'来实现平滑滚动,缓解滚动过界的情况。但如果需要更精确的控制滚动,可以使用window.scroll()方法,并设置滚动条的滚动距离。
如果我的回答解决了您的问题,请采纳!