纯JavaScript - 获取多行文本框中当前行的起始位置

3
我希望您能提供一份纯粹的 JavaScript 解决方案,因为它反映了我的项目范围。jQuery 的答案将不被标记为正确答案,但欢迎以后有类似问题的用户提问。顺便提一句:我也不感兴趣第三方库。
我试图获取多行文本框中当前行(基于 selectionStart,用户选择超过 1 行的几率)的第一个位置(0),但要将其转换为光标索引。
我试过什么?没有什么优雅的方法:
for ( i = 0; i < this.selectionStart; i++ ) {      
  if (this.value.substr(i,1).match(/\r?\n|\r/)) {
    lineStartIndx = i + 1
  }
}

当迭代具有大量行的文本区域时,这将证明是昂贵的。我的个人使用不会在每次按下键时执行,但我只是用它作为一个例子。是否有任何更好的方法,内置或其他方法来模拟这个结果?

完整示例:

var input = document.getElementById("ta");
  var output = document.getElementById("output");
  let pattern = /\r?\n|\r/;
  var lineNum, lineStartIndx;
 
  input.addEventListener("keydown", function(e) {
    taHandler(this);  
  })

  input.addEventListener("click", function(e) {
    taHandler(this);
  })

  function taHandler(elem) {

    lineNum = getLineNumForSelection(elem);

    let caretPos = elem.selectionStart;
    lineStartIndx = 0;

    for ( i = 0; i < caretPos; i++ ) {

      if (elem.value.substr(i,1).match(pattern)) {
        lineStartIndx = i + 1
      }
    }

    output.innerHTML = "Selection Start: " + caretPos + " Selection End: " + elem.selectionEnd + 
    " <br> Line Number: "  + lineNum.start + 
    "<br>Line Start Position: " + lineStartIndx;
  }

  function getLineNumForSelection(sel) {
    return {
      'start' : sel.value.substr(0, sel.selectionStart).split(pattern).length,
      'end' : sel.value.substr(0,sel.selectionEnd).split(pattern).length
    };
  }
<textarea id="ta" rows="5" cols="50">
  Line one
    Line two
    Line three
  Line four  
</textarea>
<hr>
<div id="output"></div>


这段代码能够运行吗?只是速度慢吗?(如果是的话,你应该将其发布到代码审查而不是在这里) - Cody G
document.getElementById("output").split('\n').charAt(0)? - Meghan
提供我们尝试过的事情是Stack Overflow问题的核心要求。@CodyG。我可以重新表述问题,询问一个具体的答案“如何使用Regex或textNode或offSets找到...?”然而,我觉得这会压制潜在的通用解决方案。我对javascript很新,所以我尝试的东西不是特定于javascript的。它是编程循环逻辑,不是特定于javascript解决方案。 - soulshined
感谢您的建议@SeanDenny,您的反馈回答了一个不同类型的问题。我不想要字符,而是在文本区域范围值中的索引。(抱歉,我不知道如何更好地表达它) - soulshined
1
我认为你应该给出一些“案例示例”——输入是什么,输出是什么。 - Cody G
1个回答

1
我的代码片段中的方法将内容分成行,并使用.length属性而不是循环,因此看起来更“漂亮”,但根据javascript的时间复杂度 .length可能不会更快,因为规范中没有防止浏览器实现缓慢的内容。
关于代码的两个注意事项,我会使用lineStartIndex而不是lineStartIndx,不带e。由于lineNum是一个数组,我会使用lineNumArray或selLineNums或其他更明显的变量,以num结尾的变量通常是整数。

var input = document.getElementById("ta");
  var output = document.getElementById("output");
  let pattern = /\r?\n|\r/;
  var lineNum, lineStartIndx;
 
  input.addEventListener("keydown", function(e) {
    taHandler(this);  
  })

  input.addEventListener("click", function(e) {
    taHandler(this);
  })

  function taHandler(elem) {

    lineNum = getLineNumForSelection(elem);

    let caretPos = elem.selectionStart;
    lineStartIndx = 0;

    // begin modified code
    let lines = elem.value.split(pattern),
        lineIndex = 0;
        
    while ( (lineIndex + 1 ) < lineNum.start ) {
        lineStartIndx += parseInt( lines[lineIndex].length ) + 1;
        lineIndex++;
    }
    // end modified code
  
    // begin replaced code
    for ( i = 0; i < caretPos; i++ ) {

      if (elem.value.substr(i,1).match(pattern)) {
        lineStartIndx = i + 1
      }
    }
    // end replaced code

    output.innerHTML = "Selection Start: " + caretPos + " Selection End: " + elem.selectionEnd + 
    " <br> Line Number: "  + lineNum.start + 
    "<br>Line Start Position: " + lineStartIndx;
  }

  function getLineNumForSelection(sel) {
    return {
      'start' : sel.value.substr(0, sel.selectionStart).split(pattern).length,
      'end' : sel.value.substr(0,sel.selectionEnd).split(pattern).length
    };
  }
<textarea id="ta" rows="5" cols="50">
  Line one
    Line two
    Line three
  Line four  
</textarea>
<hr>
<div id="output"></div>


这是一个公平的重构。+1 谢谢 - soulshined

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