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

时间:2019-02-16 17:42:53

标签: javascript html

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

3 个答案:

答案 0 :(得分: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>

答案 1 :(得分:3)

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

*编辑:您也可以使用em来设置input的宽度,该宽度与字体大小成比例。 (名称em最初是指所用字体和大小的大写字母M的宽度,通常与磅值ref相同)。em和{ {1}}会大致估算出您可能要达到的目标。

但是,如果您真的希望能够限制基于输入的文本长度,那么这将作为开始的非常简化示例。

编辑:我推荐@ mel-macaluso的回答:他添加了一个使用CanvasRenderingContext2D.measureText()的示例,我怀疑它比maxlength的效率高得多。

首先免责声明:

  • 此示例未考虑剪贴板操作。这是一个很大的问题,您将要谈论很多代码来尝试解决这个问题(远远超出了此处可以合理完成的范围)。

  • 这也是相当耗费资源的。进行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();
  }
})

那么为什么做这样的事情为什么这么复杂?字体是棘手的事情。您有variable width (proportional) fontskerningligatures等。这非常复杂,浏览器不提供对大多数此类信息的访问。

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

答案 2 :(得分: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>