添加文本后缀 <input type="number">
Add a text suffix to <input type="number">
我目前有很多这样的输入:
<input type="number" id="milliseconds">
此输入字段用于表示以毫秒为单位的值。
然而,我确实有多个数字输入,它们以 dB 或百分比为单位取值。
<input type="number" id="decibel">
<input type="number" id="percentages">
我想做的是在输入字段中添加一个类型后缀,让用户知道输入代表什么样的值。像这样:
(此图像经过编辑以显示我想要的结果,我也隐藏了输入类型的向上和向下箭头)。
我已经尝试 Google 这个,但我似乎找不到任何相关信息。有谁知道这是否可行,以及如何完成这样的事情?
如果您可以选择向输入添加元素,那么您可以试试这个:
.container {
max-width: 208px; /*adjust it*/
margin: auto;
position: relative;
display: inline-block;
}
#milliseconds {
padding-right: 35px;
}
.ms {
position: absolute;
top: 0;
right: 10px;
}
<div class="container">
<input type="text" id="milliseconds">
<span class="ms">ms</span>
</div>
您可以为每个输入元素使用包装器 <div>
,并将该单元定位为伪元素 ::after
,并使用相应单元的 content
。
这种方法适用于绝对定位的伪元素,不会影响现有布局。然而,这种方法的缺点是,您必须确保用户输入的长度不与文本字段一样长,否则单位会令人不快地显示在上方。对于固定的用户输入长度,它应该可以正常工作。
/* prepare wrapper element */
div {
display: inline-block;
position: relative;
}
/* position the unit to the right of the wrapper */
div::after {
position: absolute;
top: 2px;
right: .5em;
transition: all .05s ease-in-out;
}
/* move unit more to the left on hover or focus within
for arrow buttons will appear to the right of number inputs */
div:hover::after,
div:focus-within::after {
right: 1.5em;
}
/* handle Firefox (arrows always shown) */
@supports (-moz-appearance:none) {
div::after {
right: 1.5em;
}
}
/* set the unit abbreviation for each unit class */
.ms::after {
content: 'ms';
}
.db::after {
content: 'db';
}
.percent::after {
content: '%';
}
<div class="ms">
<input type="number" id="milliseconds" />
</div>
<hr />
<div class="db">
<input type="number" id="decibel" />
</div>
<hr />
<div class="percent">
<input type="number" id="percentages">
</div>
如果你想支持那些根本不显示这些箭头的浏览器,请使用 @supports or media queries。
另一种有趣的方法是使用一点 JavaScript 以使后缀真正贴在输入文本上(这可能看起来更好):
const inputElement = document.getElementById('my-input');
const suffixElement = document.getElementById('my-suffix');
inputElement.addEventListener('input', updateSuffix);
updateSuffix();
function updateSuffix() {
const width = getTextWidth(inputElement.value, '12px arial');
suffixElement.style.left = width + 'px';
}
/**
* Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
*
* @param {String} text The text to be rendered.
* @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
*
* @see
*/
function getTextWidth(text, font) {
// re-use canvas object for better performance
var canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
var context = canvas.getContext("2d");
context.font = font;
var metrics = context.measureText(text);
return metrics.width;
}
#my-input-container {
display: inline-block;
position: relative;
font: 12px arial;
}
#my-input {
font: inherit;
}
#my-suffix {
position: absolute;
left: 0;
top: 3px;
color: #555;
padding-left: 5px;
font: inherit;
}
<div id="my-input-container">
<input type="number" id="my-input" value="1500">
<span id="my-suffix">ms.</span>
</div>
但是,这只是概念验证。您将需要进一步对其进行处理以使其可以投入生产,例如使它成为一个可重复使用的插件。
此外,您还需要处理输入元素溢出的情况。
我有一个案例,设计团队希望后缀随值浮动。我们使用的是数字宽度非常不均匀的自定义字体。我想到了一个想法,即使用 ghost 来跟踪输入宽度,并通过使用包装元素用 max-width
限制溢出。这仍然是一些正在进行的工作和小故障(没有初始填充等)。
const fillBuffer = (e) => {
// Clear the buffer if input gets wiped
if (e.target.value.length === 0) {
e.target.parentElement.querySelector('.suffix span').textContent = "";
return;
}
// Using a filler char will prevent the suffix to be overwritten with the input
const extraFiller = e.target.value.length ? '1' : '';
e.target.parentElement.querySelector('.suffix span').textContent = e.target.value + extraFiller;
}
// Attach the listeners
document.querySelectorAll('input').forEach((el) => {
el.addEventListener('keydown', fillBuffer);
el.addEventListener('keyup', fillBuffer);
});
* {
font-size: 1em;
font-family: Papyrus, sans-serif;
box-sizing: border-box;
}
body {
padding: 1em;
}
.input-wrapper {
margin-bottom: 0.5em;
}
.input-wrapper.with-suffix {
position: relative;
max-width: 150px;
overflow: hidden;
}
.input-wrapper.with-suffix input {
margin: 0;
width: 100%;
outline: 0;
padding-left: 4px;
padding-right: 16px;
}
.input-wrapper.with-suffix .suffix {
position: absolute;
padding-left: 6px;
top: 2px;
pointer-events: none;
width: 100%;
}
.input-wrapper.with-suffix .suffix span {
user-select: none;
pointer-events: none;
}
.input-wrapper.with-suffix .suffix .filler {
display: inline-block;
white-space: pre; /* Allow more than two whitespaces to be rendered */
color: rgba(0, 0, 0, 0.0);
background: rgba(255, 0, 0, 0.3);
max-width: calc(100% - 16px);
}
<div class="input-wrapper with-suffix">
<input type="text" value="5000">
<div class="suffix"><span class="filler">5000</span><span>€</span></div>
</div>
<div class="input-wrapper with-suffix">
<input type="text" value="5000">
<div class="suffix"><span class="filler">5000</span><span>€</span></div>
</div>
我目前有很多这样的输入:
<input type="number" id="milliseconds">
此输入字段用于表示以毫秒为单位的值。 然而,我确实有多个数字输入,它们以 dB 或百分比为单位取值。
<input type="number" id="decibel">
<input type="number" id="percentages">
我想做的是在输入字段中添加一个类型后缀,让用户知道输入代表什么样的值。像这样:
(此图像经过编辑以显示我想要的结果,我也隐藏了输入类型的向上和向下箭头)。
我已经尝试 Google 这个,但我似乎找不到任何相关信息。有谁知道这是否可行,以及如何完成这样的事情?
如果您可以选择向输入添加元素,那么您可以试试这个:
.container {
max-width: 208px; /*adjust it*/
margin: auto;
position: relative;
display: inline-block;
}
#milliseconds {
padding-right: 35px;
}
.ms {
position: absolute;
top: 0;
right: 10px;
}
<div class="container">
<input type="text" id="milliseconds">
<span class="ms">ms</span>
</div>
您可以为每个输入元素使用包装器 <div>
,并将该单元定位为伪元素 ::after
,并使用相应单元的 content
。
这种方法适用于绝对定位的伪元素,不会影响现有布局。然而,这种方法的缺点是,您必须确保用户输入的长度不与文本字段一样长,否则单位会令人不快地显示在上方。对于固定的用户输入长度,它应该可以正常工作。
/* prepare wrapper element */
div {
display: inline-block;
position: relative;
}
/* position the unit to the right of the wrapper */
div::after {
position: absolute;
top: 2px;
right: .5em;
transition: all .05s ease-in-out;
}
/* move unit more to the left on hover or focus within
for arrow buttons will appear to the right of number inputs */
div:hover::after,
div:focus-within::after {
right: 1.5em;
}
/* handle Firefox (arrows always shown) */
@supports (-moz-appearance:none) {
div::after {
right: 1.5em;
}
}
/* set the unit abbreviation for each unit class */
.ms::after {
content: 'ms';
}
.db::after {
content: 'db';
}
.percent::after {
content: '%';
}
<div class="ms">
<input type="number" id="milliseconds" />
</div>
<hr />
<div class="db">
<input type="number" id="decibel" />
</div>
<hr />
<div class="percent">
<input type="number" id="percentages">
</div>
如果你想支持那些根本不显示这些箭头的浏览器,请使用 @supports or media queries。
另一种有趣的方法是使用一点 JavaScript 以使后缀真正贴在输入文本上(这可能看起来更好):
const inputElement = document.getElementById('my-input');
const suffixElement = document.getElementById('my-suffix');
inputElement.addEventListener('input', updateSuffix);
updateSuffix();
function updateSuffix() {
const width = getTextWidth(inputElement.value, '12px arial');
suffixElement.style.left = width + 'px';
}
/**
* Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
*
* @param {String} text The text to be rendered.
* @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
*
* @see
*/
function getTextWidth(text, font) {
// re-use canvas object for better performance
var canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
var context = canvas.getContext("2d");
context.font = font;
var metrics = context.measureText(text);
return metrics.width;
}
#my-input-container {
display: inline-block;
position: relative;
font: 12px arial;
}
#my-input {
font: inherit;
}
#my-suffix {
position: absolute;
left: 0;
top: 3px;
color: #555;
padding-left: 5px;
font: inherit;
}
<div id="my-input-container">
<input type="number" id="my-input" value="1500">
<span id="my-suffix">ms.</span>
</div>
但是,这只是概念验证。您将需要进一步对其进行处理以使其可以投入生产,例如使它成为一个可重复使用的插件。
此外,您还需要处理输入元素溢出的情况。
我有一个案例,设计团队希望后缀随值浮动。我们使用的是数字宽度非常不均匀的自定义字体。我想到了一个想法,即使用 ghost 来跟踪输入宽度,并通过使用包装元素用 max-width
限制溢出。这仍然是一些正在进行的工作和小故障(没有初始填充等)。
const fillBuffer = (e) => {
// Clear the buffer if input gets wiped
if (e.target.value.length === 0) {
e.target.parentElement.querySelector('.suffix span').textContent = "";
return;
}
// Using a filler char will prevent the suffix to be overwritten with the input
const extraFiller = e.target.value.length ? '1' : '';
e.target.parentElement.querySelector('.suffix span').textContent = e.target.value + extraFiller;
}
// Attach the listeners
document.querySelectorAll('input').forEach((el) => {
el.addEventListener('keydown', fillBuffer);
el.addEventListener('keyup', fillBuffer);
});
* {
font-size: 1em;
font-family: Papyrus, sans-serif;
box-sizing: border-box;
}
body {
padding: 1em;
}
.input-wrapper {
margin-bottom: 0.5em;
}
.input-wrapper.with-suffix {
position: relative;
max-width: 150px;
overflow: hidden;
}
.input-wrapper.with-suffix input {
margin: 0;
width: 100%;
outline: 0;
padding-left: 4px;
padding-right: 16px;
}
.input-wrapper.with-suffix .suffix {
position: absolute;
padding-left: 6px;
top: 2px;
pointer-events: none;
width: 100%;
}
.input-wrapper.with-suffix .suffix span {
user-select: none;
pointer-events: none;
}
.input-wrapper.with-suffix .suffix .filler {
display: inline-block;
white-space: pre; /* Allow more than two whitespaces to be rendered */
color: rgba(0, 0, 0, 0.0);
background: rgba(255, 0, 0, 0.3);
max-width: calc(100% - 16px);
}
<div class="input-wrapper with-suffix">
<input type="text" value="5000">
<div class="suffix"><span class="filler">5000</span><span>€</span></div>
</div>
<div class="input-wrapper with-suffix">
<input type="text" value="5000">
<div class="suffix"><span class="filler">5000</span><span>€</span></div>
</div>