如何在JavaScript的输入框值改变时的回调中显示等待光标?

前端开发 2026-07-11

在输入元素的值改变回调结束时,我正在调用一个耗时的计算,并且想显示一个等待光标。我尝试过的方法是将document.body.className设置为 'waiting';并在CSS中有

body.waiting * {cursor:wait!important;}

然后 document.body.focus();,并最终把耗时计算(ldc)函数放在一个 setTimeout(ldc,1); 里面,这样在计算开始前屏幕就会被重新绘制。 (下面的示例中,ldc被称为 'plot',并使用一个100的超时而不是1;似乎并不相关。) ldc以 document.body.className = ''; 结束

但在该计算过程中并不会显示等待光标。如果我不执行 body.focus(),插入点就只会出现在输入元素上。

当我在另一个元素上的按钮点击回调中做几乎相同的事情时,我确实看到了预期的等待光标。

是否有办法在每次输入变化事件发生时都显示等待光标?

下面给出一个相对最简的代码来演示问题。引发问题的行为是通过单击一个输入元素、用键盘编辑其文本,并以回车或Tab结束来演示的。最相关的部分是updateLimits函数,它被用作#go按钮(点击时)的回调,以及输入元素(change时)的回调。触发输入元素改变有多种方式;当change回调被回车或Tab触发时会出现问题,但即使点击在改变的输入元素之外(即使点击的是另一个输入元素),也不会触发。

var body; // #body
var pcv; // canvas
var pcx; // context
const side = 513;

function init() { // initialize the world
  body = document.body;
  initplot();
}

function initplot() {
  pcv = document.getElementById('plotarea');
  pcv.width = side;
  pcv.height = side;
  pcx = pcv.getContext('2d');
  pcx.lineWidth = 1;
  pcx.lineCap = 'round';
  pcx.lineJoin = 'round';
  pcx.strokeStyle = 'black';
  document.getElementById('bot').value = -1;
  document.getElementById('top').value = 1;
  document.getElementById('lft').value = -1;
  document.getElementById('rgt').value = 1;
  b = document.getElementsByTagName('input');
  for (i = 0; i < b.length; i++) {
    b[i].step = .125;
    b[i].addEventListener('change', updateLimits);
  }
  document.getElementById('go').addEventListener('click', updateLimits);
}

function updateLimits(event) {
  event.preventDefault();
  event.stopPropagation();
  body.focus();
  body.className = 'waiting';
  setTimeout(plot, 100);
}

function plot() { // just take some time
  var n;
  for (n = 1; n < 999999; n++) {
    factor(n, Math.sqrt(n) + 1);
  }
  body.className = '';
}

function factor(n, b) { // factor n into primes up to (but not including) b
  if (!Number.isInteger(n) || n < 1) {
    throw new TypeError('Only positive integers can be factored');
  }
  var f = {};
  if (n == 1) return f;
  for (var p = 2; p < b; p = (p + 1) | 1) {
    while (!(n % p)) {
      f[p] = (f[p] || 0) + 1;
      n /= p;
    }
    if (n == 1) return f;
  }
  f[n] = 1;
  return f;
}
body {
  touch-action: none;
}

body.waiting * {
  cursor: wait !important;
}

#canvas {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
}

.varlabel {
  font-family: serif;
  font-style: italic;
  text-align: center;
  margin: 0;
}

#x {
  grid-row-start: 4;
  grid-column-start: 3;
}

#y {
  grid-row-start: 2;
  grid-column-start: 1;
  align-self: center;
}

#plot {
  float: left;
  display: grid;
  grid-template-columns: 20px 100px 313px 100px;
  grid-template-rows: 100px 313px 100px 20px;
  margin-top: 10px;
  margin-left: 10px;
}

input[type=number]::-webkit-outer-spin-button,
input[type=number]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

input[type=number] {
  width: 100px;
  height: 20px;
  margin: 0;
  box-sizing: border-box;
  -moz-appearance: textfield;
  appearance: textfield;
}

#top {
  grid-column-start: 1;
  grid-row-start: 1;
  transform: rotate(-90deg);
  transform-origin: 50px 50px;
  text-align: right;
}

#bot {
  grid-column-start: 1;
  grid-row-start: 3;
  transform: rotate(-90deg);
  transform-origin: 50px 50px;
  text-align: left;
}

#lft {
  grid-column-start: 2;
  grid-row-start: 4;
  text-align: left;
}

#rgt {
  grid-column-start: 4;
  grid-row-start: 4;
  text-align: right;
}

#plotarea {
  grid-column-start: 2;
  grid-column-end: span 3;
  grid-row-start: 1;
  grid-row-end: span 3;
  width: 513px;
  height: 513px;
}
<!DOCTYPE HTML>
<html lang="en">

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />


</head>

<body onload='init(this)'>
  <button id='go'>GO</button>
  <div id='plot'>
    <input id='top' class='lim' type='number'></input>
    <p id='y' class='varlabel'>y</p>
    <input id='bot' class='lim' type='number'></input>
    <canvas id='plotarea'></canvas>
    <input id='lft' class='lim' type='number'></input>
    <p id='x' class='varlabel'>x</p>
    <input id='rgt' class='lim' type='number'></input>
  </div>
</body>

</html>

解决方案

问题在于,当你在输入字段中按下Enter或 Tab时,浏览器在你的 setTimeout 运行之前实际上并不会更新光标,即使有延迟也一样。请改用 requestAnimationFrame

function updateLimits(event) {
  event.preventDefault();
  event.stopPropagation();
  body.className = 'waiting';

  requestAnimationFrame(() => {
    plot();
  });
}

这会告诉浏览器在下一次屏幕更新之前的时刻等待,这样它就有时间真正显示等待光标。如果仍然不起作用,尝试先让输入框失焦:

function updateLimits(event) {
  event.preventDefault();
  event.stopPropagation();

  if (document.activeElement) {
    document.activeElement.blur();
  }

  body.className = 'waiting';

  requestAnimationFrame(() => {
    plot();
  });
}

之所以在按钮点击时有效而在Enter/Tab时无效,是因为点击按钮会自动从输入框移除焦点,而Enter/Tab会保持焦点。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章