javascript 如何在textarea上创建go to line和find text活动

68bkxrlz  于 2023-09-29  发布在  Java
关注(0)|答案(2)|浏览(76)

我正试图在一个使用textarea和编号div的编辑器上添加go to line和find text功能。
我已经尝试了下面的代码,但它并没有滚动编辑器来关注用户在提示符中输入的行或文本;
请帮助我在编辑器上实现go to line和find text函数。

const ta = document.querySelector('textarea')

function updateRowNumbering() {
  const num = ta.value.split("\n").length;
  const numbers = ta.parentNode.querySelector(".numbers");
  numbers.innerHTML = Array(num).fill("<span></span>").join("");
}

ta.value = `One
Two
Three
Four
Five
Six
Seven
Eight
Nine
Ten
Eleven
Kumi na Mbili
`
updateRowNumbering()

//Trying to implement go to line and find text;
const numbers = ta.parentNode.querySelector('.numbers');
const gotoLinePrompt = document.querySelector('.goto-line-prompt');
const findPrompt = document.querySelector('.find-prompt');

// Hide the prompts by default.
gotoLinePrompt.style.display = 'none';
findPrompt.style.display = 'none';

// Add event listeners to the "Go to Line" and "Find" buttons.
document.querySelector('.goto-line-button').addEventListener('click', function() {
  gotoLinePrompt.style.display = 'block';
});

document.querySelector('.find-button').addEventListener('click', function() {
  findPrompt.style.display = 'block';
});

// Add event listeners to the "OK" buttons in the prompts.
gotoLinePrompt.querySelector('.ok-button').addEventListener('click', function() {
  const lineNumber = gotoLinePrompt.querySelector('input').value;
  if (lineNumber !== null) {
    const lineHeight = ta.scrollHeight / ta.clientHeight;
    ta.scrollTop = (lineNumber - 1) * lineHeight;
    numbers.scrollTop = ta.scrollTop;
  }

  gotoLinePrompt.style.display = 'none';
});

findPrompt.querySelector('.ok-button').addEventListener('click', function() {
  const searchTerm = findPrompt.querySelector('input').value;
  if (searchTerm !== null) {
    const text = ta.textContent;
    const index = text.indexOf(searchTerm);
    if (index !== -1) {
      const lineNumber = Math.floor(index / ta.value.split('\n')[0].length) + 1;
      ta.scrollTop = (lineNumber - 1) * lineHeight;
      numbers.scrollTop = ta.scrollTop;
    }
  }

  findPrompt.style.display = 'none';
});
body, textarea {
  font-family: Consolas, "Courier New", Courier, monospace;
}

.editor {
  display: inline-grid;
  grid-template-columns: 3em auto;
  gap: 10px;
  line-height: 21px;
  background: rgb(40 42 58);
  border-radius: 2px;
  overflow-y: auto;
  max-height: 250px;
}

.editor>* {
  padding-top: 10px;
  padding-bottom: 10px;
}

.numbers {
  text-align: right;
  background: #333;
  padding-right: 5px;
}

.numbers span {
  counter-increment: linenumber;
}

.numbers span::before {
  content: counter(linenumber);
  display: block;
  color: #888;
}

textarea {
  line-height: 21px;
  border: 0;
  background: transparent;
  color: #fff;
  min-width: 500px;
  outline: none;
  resize: none;
  padding-right: 10px;
}
<div class="actions">
<div class="goto-line-prompt" style="display: none;">
  <h1>Go to Line</h1>
  <input type="number" placeholder="Enter the line number to go to:" />
  <button type="button" class="ok-button">OK</button>
</div>
<div class="find-prompt" style="display: none;">
  <h1>Find</h1>
  <input type="text" placeholder="Enter the text to find:" />
  <button type="button" class="ok-button">OK</button>
</div>
<button class="goto-line-button">Go to Line</button>
<button class="find-button">Find</button>
</div>
<div class="editor">
 <div class="numbers">
      <span></span>
   </div>
 <textarea wrap="off" onkeyup="updateRowNumbering()"></textarea>
</div>
iklwldmw

iklwldmw1#

.value永远不会为空,所以检查是无用的。文本区域没有textContent
有内置的api可以滚动到视图中的元素,所以没有理由重新发明轮子。

const ta = document.querySelector('textarea')

function updateRowNumbering() {
  const num = ta.value.split("\n").length;
  const numbers = ta.parentNode.querySelector(".numbers");
  numbers.innerHTML = Array(num).fill("<span></span>").join("");
}

ta.value = `One
Two
Three
Four
Five
Six
Seven
Eight
Nine
Ten
Eleven
Kumi na Mbili
a
b
c
d
e
f
g
h
i
j
k
l
foo bar
`
updateRowNumbering()

//Trying to implement go to line and find text;
const numbers = ta.parentNode.querySelector('.numbers');
const gotoLinePrompt = document.querySelector('.goto-line-prompt');
const findPrompt = document.querySelector('.find-prompt');

// Hide the prompts by default.
gotoLinePrompt.style.display = 'none';
findPrompt.style.display = 'none';

// Add event listeners to the "Go to Line" and "Find" buttons.
document.querySelector('.goto-line-button').addEventListener('click', function() {
  gotoLinePrompt.style.display = 'block';
});

document.querySelector('.find-button').addEventListener('click', function() {
  findPrompt.style.display = 'block';
});

function scrollToLine(lineNumber) {
  if (!lineNumber || lineNumber < 1) return;
  const nums = document.querySelectorAll(".editor > .numbers > span");
  const selectedNum = nums[lineNumber - 1];
  if (!selectedNum) return;
  selectedNum.scrollIntoView();
}

// Add event listeners to the "OK" buttons in the prompts.
gotoLinePrompt.querySelector('.ok-button').addEventListener('click', function() {
  const lineNumber = Number(gotoLinePrompt.querySelector('input').value.trim());
  scrollToLine(lineNumber);
  gotoLinePrompt.style.display = 'none';
});

findPrompt.querySelector('.ok-button').addEventListener('click', function() {
  const searchTerm = findPrompt.querySelector('input').value.trim();
  if (!searchTerm.length) return;
  const textLines = ta.value.split('\n');
  
  const lineNumber = textLines.findIndex(text => text.includes(searchTerm));
  
  if (lineNumber > -1) {
    scrollToLine(lineNumber + 1);
  }

  findPrompt.style.display = 'none';
});
body,
textarea {
  font-family: Consolas, "Courier New", Courier, monospace;
}

.editor {
  display: inline-grid;
  grid-template-columns: 3em auto;
  gap: 10px;
  line-height: 21px;
  background: rgb(40 42 58);
  border-radius: 2px;
  overflow-y: auto;
  max-height: 250px;
}

.editor>* {
  padding-top: 10px;
  padding-bottom: 10px;
}

.numbers {
  text-align: right;
  background: #333;
  padding-right: 5px;
}

.numbers span {
  counter-increment: linenumber;
}

.numbers span::before {
  content: counter(linenumber);
  display: block;
  color: #888;
}

textarea {
  line-height: 21px;
  border: 0;
  background: transparent;
  color: #fff;
  min-width: 500px;
  outline: none;
  resize: none;
  padding-right: 10px;
}
<div class="actions">
  <div class="goto-line-prompt" style="display: none;">
    <h1>Go to Line</h1>
    <input type="number" placeholder="Enter the line number to go to:" />
    <button type="button" class="ok-button">OK</button>
  </div>
  <div class="find-prompt" style="display: none;">
    <h1>Find</h1>
    <input type="text" placeholder="Enter the text to find:" />
    <button type="button" class="ok-button">OK</button>
  </div>
  <button class="goto-line-button">Go to Line</button>
  <button class="find-button">Find</button>
</div>
<div class="editor">
  <div class="numbers">
    <span></span>
  </div>
  <textarea wrap="off" onkeyup="updateRowNumbering()"></textarea>
</div>
beq87vna

beq87vna2#

我在这里看到了一些问题,但我认为问题的核心是,当您应该针对编辑器组件时,您却针对要滚动的文本区域。你会注意到,如果你在.editor(不是.editor>*)上设置height/min-height,比如40 px,你会得到一个双滚动条的场景,文本区域滚动,编辑器也滚动-如果你试图滚动数字,整个编辑器滚动,而当关注文本区域时,只有文本滚动。
在方向上,这样的东西可能会有所帮助:

const editor = document.querySelector('.editor')
// ^ add another selector for editor
const ta = document.querySelector('textarea')
...

gotoLinePrompt.querySelector('.ok-button').addEventListener('click', function() {
  const lineNumber = gotoLinePrompt.querySelector('input').value;
  if (lineNumber !== null) {
    const lineHeight = 21; // I hard-coded this to test, but your math may still work
    editor.scrollTop = (lineNumber - 1) * lineHeight;
    // ^ scroll the whole editor instead
...
.editor {
  ...
  /* Add a specific height property here to test */
  height: 70px; 
}

textarea {
  ...
  /* Add overflow: hidden here to make sure the text area doesn't scroll on its own */
  overflow: hidden;
}

+1也对@epascarello的回答与点不重新发明滚动API,这将有助于确保事情正确滚动无论你最终滚动的元素。

相关问题