如何检测可以完全适合预定义文本框长度的字符数?

问题描述 投票:0回答:3

假设我添加了一个长度为50px的文本框,并且我想计算完全适合该文本框内的确切字符数(包括空格),我的意思是不应该允许在需要该文本框的文本框中键入字符整条线向左滑动;我的意思是,在另一个词中,我们需要禁止打字员进一步插入任何字母,因为该行达到文本框的长度。我们能用JavaScrip解决这个问题吗?感谢您的帮助,任何帮助将不胜感激。

javascript html
3个回答
3
投票

整个逻辑是有缺陷的,因为它还取决于输入内部文本的大小。相反,我会输入一个不超出的字符限制。使用maxlength输入属性。

无论如何,如果你真的想走这条路线,我认为这是一种矫枉过正而且不需要,那么你可以:

  • 利用CanvasRenderingContext2D.measureTextdocs here
  • 为了做到这一点,你必须创建一个隐藏的canvas element来模仿你的输入文本。
  • 之后,如果文本超出输入宽度,则需要检查输入,并避免任何进一步的击键但仍允许删除。

查找附加的示例代码段,未优化,我正在谈论的内容。

const form = document.querySelector('#form'),
      input = form.querySelector('input')

  const createAppendCanvas = form => {
    const canvas = document.createElement('Canvas')
    form.appendChild(canvas)
  }

  createAppendCanvas(form)

  const getTextMetrics = inputText => {
    const canvas = document.querySelector('canvas'),
          textWidth = Math.ceil(canvas.getContext('2d').measureText(inputText).width) + 10
    return textWidth
  }

  const disableTyping = (event, input) => {
    const inputText = event.target.value,
          inputWidth = input.clientWidth

    if (getTextMetrics(inputText) >= inputWidth) {
      event.preventDefault()
      return false
    }

  }

  input.addEventListener('keypress', event => disableTyping(event, input))
input {
  width: 50px;
}

canvas {
  display: none;
}
<form id="form">
  <input type="text" />
</form>

3
投票

正如@ mel-macaluso正确指出的那样,这是一个非常大的兔子洞,标准做法是使用maxlength属性来限制字符数。

*编辑:您还可以使用input设置em的宽度,该宽度与字体大小成比例。 (名称em最初是对使用的字体和大小的大写字母M的宽度的引用,它通常与点大小ref相同)emmaxlength中的宽度组合将给出非常粗略的近似值你可能想要实现。

但是,如果您真的希望能够限制基于输入的文本长度,那么这将是一个非常简单的示例,说明如何开始。

Edit: I recommend @mel-macaluso's answer: he added an example using CanvasRenderingContext2D.measureText(), which I suspect is much more efficient than getBoundingClientRect.

首先是一些免责声明:

  • 此示例未考虑剪贴板操作。这是一个非常大的问题,你会谈论更多的代码来试图解释它(超出了可以在这里合理完成的范围)。
  • 这也是资源密集型的。执行getBoundingClientRect的过程会强制浏览器重新加载文档内容。根据页面的大小,这可能是一个大问题,而且不是轻易做的事情。

var inp = document.getElementById('test');
// get font for input
var style = getComputedStyle(inp);
var maxWidth = inp.getBoundingClientRect().width;
var sizeTest = document.createElement('span');
// set font for span to match input
sizeTest.style.font = style.font;
inp.addEventListener('keydown', function(e){
  if(e.ctrlKey || e.altKey) return;
  if(e.key && e.key.length===1) {
    sizeTest.textContent = inp.value;
    document.body.append(sizeTest);
    var w = sizeTest.getBoundingClientRect().width;
    sizeTest.remove();
    console.log(maxWidth, w, e.key, e.code);
    if(w>maxWidth) e.preventDefault();
  }
})
<input id='test'/>

那么为什么做这样的事情会如此复杂呢?字体是棘手的事情。你有variable width (proportional) fontskerningligatures等。它非常复杂,浏览器无法访问大部分信息。

因此,如果您想知道一段文本的长度,通常必须将其放在具有相同字体设置的范围内,然后请求边界尺寸。


2
投票

这是使用嵌套跨度(带有contenteditable内跨)作为代理输入的简洁解决方案。

// Identifiers and dynamic styling
const innerSpan = document.querySelector("span.inner"),
      outerSpan = document.querySelector("span.outer");
  /* Threshold should be at least one character-width less than outerSpan.
     (This formula was pretty close for my few tests;
      for more precision and less flexibility, you can hard-code a value.) */
const estMaxCharWidth = innerSpan.offsetHeight / 1.7,
      thresholdWidth = outerSpan.offsetWidth - estMaxCharWidth;
innerSpan.style.minWidth = `${Math.floor(thresholdWidth)-3}px`; // defaults to 0 
innerSpan.style.minHeight = `${Math.floor(outerSpan.offsetHeight)-2}px`

// Listeners
innerSpan.addEventListener("focus", customOutline);
innerSpan.addEventListener("keydown", checkKeyAndWidth);
innerSpan.addEventListener("blur", removeOutlineAndHandleText);


// Functions
function checkKeyAndWidth(e){
  // Runs when user presses a key, Conditionally prevents input
  if(e.code == "Enter" || e.keyCode == 13){
    e.preventDefault(); // Don't insert a new line
    e.target.blur(); // (In production, set the focus to another element)
  }
  else{
    // Some keys besides Enter are important, More could be added
    const whitelistCodes = ["Backspace", "Tab", "Escape", "ArrowLeft", "ArrowRight", "Insert", "Delete"];
    const whitelistKeyCodes = [8,9,27,37,39,45,46];
    // If the inner span is wide enough, stop accepting characters
    let acceptingCharacters = e.target.offsetWidth <= thresholdWidth;
    if(!acceptingCharacters && !whitelistCodes.includes(e.code) && !whitelistKeyCodes.includes(e.keyCode) && !whitelistKeyCodes.includes(e.which)){
      // Unauthorized incoming keystroke
      e.preventDefault();
    }
  }
}

function customOutline(){
  // Runs when span gets focus, Needed for accessibility due to CSS settings
  outerSpan.style.borderColor = "DeepSkyBlue"; 
}

function removeOutlineAndHandleText(){
  // Runs when focus is lost
  outerSpan.style.borderColor = "Gray";
  if(innerSpan.length < 1){ innerSpan.innerHTML = " "; } // force content
  /* Since this is not a real input element, now might be the time to do something with the entered text */
}
.outer{
  display: inline-block;
  position: relative;
  width: 100px; /* Defaults to 0 */
  padding: 0;
  border: 1px solid gray;
}
.inner{
  display: inline-block;
  position: relative;
  top: 0;
  height: 100%;
  margin: 0;
  outline: none; /* Don't do this without calling customOutline on focus */
}
<!-- requires that browser supports `contenteditable` -->
<span class="outer">
  <!-- space character in innerSpan may improve cross-browser rendering -->
	<span class="inner" contenteditable="true"> </span>
</span>
© www.soinside.com 2019 - 2024. All rights reserved.