I'm creating a simple app in which user provides a number and square is generated with the given number. The problem is that when the user provides the input the first time, it works as intended, but when the user edits the value and clicks the button, the value adds with the existing value and it displays the square of the sums. I want to reset the input after the button click, but haven't got any clue how to do. Any solutions
The html code looks like this
<div class="intro">
<h1>Select no of squares to be made</h1>
<small>This experience is better with the values between 400 and 600</small>
<input type="number" id="squaresInput">
<button class="show">Go</button>
</div>
And js file is
const userInput = document.getElementById('squaresInput')
const btnInput = document.querySelector('.show')
btnInput.addEventListener('click', () => getUserInput())
let squaresNum
function getUserInput(){
let squaresNum = userInput.value
for(let i = 0; i < squaresNum; i++){
const square = document.createElement('div')
square.classList.add('square')
square.addEventListener('mouseover', () => setColor(square))
square.addEventListener('mouseout', () => removeColor(square))
container.appendChild(square)
}
}
This should work for you :
userInput.value = ''
I'd start moving
const userInput = document.getElementById('squaresInput')
into "getUserInput()" function.
Then removing the "let squaresNum" outside the function, it is useless.
Gianluca
You can just add userInput.value = ""; at the end of function, which will clear out any existing value in the input.
const userInput = document.getElementById('squaresInput')
const btnInput = document.querySelector('.show')
btnInput.addEventListener('click', () => getUserInput())
let squaresNum;
function getUserInput(){
let squaresNum = userInput.value
for(let i = 0; i < squaresNum; i++){
const square = document.createElement('div')
square.classList.add('square')
square.addEventListener('mouseover', () => setColor(square))
square.addEventListener('mouseout', () => removeColor(square))
container.appendChild(square)
}
userInput.value = "";
}
<div class="intro">
<h1>Select no of squares to be made</h1>
<small>This experience is better with the values between 400 and 600</small>
<input type="number" id="squaresInput">
<button class="show">Go</button>
<div class="container"></div>
</div>
You need something like this before adding more squares.
container.innerHTML = ""
at the beginning of
function getUserInput () {....
In this way you will eliminate the squares previously created before creating the new ones.
Take advantage of using <form>.
Change <div class='intro'> to <form id='intro'>
Whatever .container is change it to <fieldset id='container'> or name='container'. If you don't want the border, in CSS .container {border:0;}
Now you just register the 'submit' event to the <form>
When any <button>, <input type='submit'>, or <button type='submit'> is clicked or a form control has focus and user keys Enter/Return the 'submit' event is triggered.
event.preventDefault() is to stop the <form> from trying to send data to a server.
this.reset(); will clear the <input>
Unless setColor() and removeColor() do more than just change div.square color, it's better just to use CSS. .square {border-color: red; background: red}
Using a <form> allows you to use the HTMLFormControlsCollection and HTML Forms API. It's terse and specific which allows more control and less coding.
// Reference a `<form>`
const f = document.forms['id'] /*OR*/ document.forms.id // name can be used as well
// Reference all form controls within <form>
const io = f.elements;
// Reference a form control within <form>
const btn = io['id'] /*OR*/ io.id // name can be used as well
Use createDocumentFragment(); because 400 to 600 nodes to render is really too much.
Also added a <button> to clear out the squares whenever the user wishes to.
Added 'mouseover/out' event handler. It's registered to the parent element only and not to each .square. This and the 'submit' event are possible because of Event Bubbling and how Event Delegation leverages it.
In the event handler hoverGrid() values, expressions, statements, and functions (ex. addColor() and removeColor()) can be placed within the appropriate case of the switch().
...
case 'mouseover':
addColor();
break;
case 'mouseout':
removeColor();
break;
...
const I = document.forms.intro;
const io = I.elements;
const clr = io.clr;
const box = io.box;
I.onsubmit = createGrid;
clr.onclick = clearGrid;
box.onmouseover = hoverGrid;
box.onmouseout = hoverGrid;
function createGrid(event) {
event.preventDefault();
let cnt = parseInt(io.qty.value);
let frag = document.createDocumentFragment();
for (let i = 0; i < cnt; i++) {
let square = document.createElement('div');
square.className = 'square';
square.dataset.index = i + 1;
frag.appendChild(square);
}
box.appendChild(frag);
this.reset();
console.clear();
console.log(box.childElementCount);
};
function clearGrid(event) {
this.previousElementSibling.innerHTML = '';
};
function hoverGrid(event) {
let E = event.type;
const sqr = event.target;
if (sqr.matches('.square')) {
switch (E) {
case 'mouseover':
console.log(sqr.dataset.index);
break;
case 'mouseout':
console.clear();
break;
default:
break;
}
}
};
:root {
font-size: 16px;
}
body {
font-family: Consolas;
line-height: 1;
overflow: auto;
}
input,
button {
display: inline-block;
font: inherit
}
small {
display: block;
margin-bottom: 4px;
}
button {
width: 4ch;
padding: 2px 5px;
cursor: pointer;
}
#box {
display: flex;
flex-flow: row wrap;
max-width: 96vw;
height: max-content;
}
.square {
width: 1ch;
height: 1ch;
border: 1px solid black
}
.square:hover {
border-color: red;
background: red;
}
#clr {
width: 9ch;
float: right;
}
/* SO Console Display - Rightside Column */
.as-console-wrapper {
width: 20% !important;
font-variant: normal;
font-weight: bold;
color: red;
}
.as-console-row.as-console-row::after {
content: '';
padding: 0;
margin: 0;
border: 0;
width: 0;
}
<form id="intro">
<fieldset>
<legend>Select no. of squares to be made</legend>
<small>This experience is better with the values between 400 and 600</small>
<input type="number" id="qty" min='0' max='1000'>
<button>GO</button>
</fieldset>
<fieldset id='box'></fieldset>
<button id='clr' type='button'>CLEAR</button>
</form>
I have to following Code: JSFiddle – I have an HTML-input and a button. – When clicking on the button a function is being run, that changes the input.value to this text: "Black Yellow".
I would like the word Yellow to be yellow. color: yellow
I found some stuff on stack overflow, but nothing that really worked for me and from what I've read, I think this might be a bit tricky, but I really need this to work, so I would very much welcome any sort of input! – Simon
HTML
<button onclick="myFunction()"> Run </button>
<input name="input1" id="myInput">
JS
function myFunction() {
document.querySelector("#myInput").value = "Black Yellow";
}
It is not an easy task as someone else mentioned. I have done something similar in react but that is completely different from doing it in plain javascript.
Here is how you can create your own input field:
Javascript:
const input = {
element: document.getElementById("input"),
focusing: false,
value: ""
}
function checkTarget(e) {
if (e.target !== input.element){
input.focusing = false
return
}
input.focusing = true
console.log(input.focusing)
}
function handleKey(e) {
if (e.isComposing || e.keyCode === 229 || !input.focusing) {
return;
}
input.value += e.key
input.element.innerText = input.value
}
function handleClick() {
let texts = input.value.split(" ")
let span1 = document.createElement("span")
let span2 = document.createElement("span")
span1.innerText = texts[0] + " "
span2.innerText = texts[1]
span2.style.color = "yellow"
input.element.innerText = ""
input.element.appendChild(span1)
input.element.appendChild(span2)
}
document.addEventListener("click", checkTarget)
document.addEventListener("keydown", handleKey);
HTML:
<button id="button"onclick="handleClick()"> Run </button>
<div id="input">
</div>
CSS:
#input {
width: 150px;
height: 20px;
border: #777 1px solid;
border-radius: 2px;
display: inline-block;
}
Run this in your fillde and let me know if it works.
Of course, this will work if there are only 2 words present in the "input" field. For more words you will need to tweak this code.
What i had to do in react was - create a text area which shows each word in different color (rainbow input). So i created an array of all rainbow colors, created a rand function which chooses one of them and then I did a similar thing as i did here. I used map on the array i got from split(" ") and then returned a span with inline styling for the text color. And finally rendered the entire junk in the div. That was the easy part. The editing was the hard part (handling delete, backspace, tab etc). Which is when I gave up and stole a component off of github.
This is not an easy task. You can't control the word color in HTML at the moment. You need to create your own input somehow using 2 input elements. For every color an input. But as I said, it can get complicated.
function renderFakeInput(input, colors) {
input.style.display = "none";
const fakeInput = document.createElement("div");
fakeInput.classList.add("fake-input");
fakeInput.addEventListener("click", () => renderRealInput(input, colors, fakeInput));
for (const color of colors) {
const word = document.createElement("div");
word.textContent = color;
word.style.color = color;
fakeInput.appendChild(word);
}
input.parentElement.appendChild(fakeInput);
}
function renderRealInput(input, colors, fakeInput) {
const onBlur = () => {
input.removeEventListener("blur", onBlur);
renderFakeInput(input, input.value.split(" "));
};
input.style.display = "";
input.value = colors.join(" ");
input.focus();
input.addEventListener("blur", onBlur);
fakeInput.remove();
}
const colorString = "Black Yellow";
const colors = colorString.split(" ");
const inputs = Array.from(document.getElementsByClassName("multi-color"));
for (const input of inputs) {
renderFakeInput(input, colors);
}
.fake-input {
display: flex;
flex-direction: row;
}
<div>
<input class="multi-color" />
</div>
<div>
<input class="multi-color" />
</div>
How could one get an elements CSS property (for example width/height) as it was set with CSS rules, in whatever units it was set (eg percent/em/px)? (In Google Chrome, preferably frameworkless).
Using getComputedStyle returns the current value in pixels, so does css() in jQuery.
For example:
<div class="b">first</div>
<div id="a" class="a">second</div>
<style>
div { width: 100px; }
x, div#a { width: 50%; }
.a { width: 75%; }
</style>
While iterating all div elements in this example, I'd like to be able to get the second divs width as 50% (and the first as 100px).
Chrome element inspector can display CSS property value as they were set, so it should be possible in Chrome.
Not an exact duplicate of the linked question, as there the accepted answer there is a simple hack that produces a percentage width no matter what kind of width is set. And for the rest you have to know the selector used to make the active rule? How would one know that?
It's not as simple as just calling WebKits getMatchedCSSRules(), it does return the matched rules in order of priority (altho I've seen no mention of this order in the docs), but the order does not take regard to property important priority and does not include element styles. So I ended up with this function:
getMatchedStyle
function getMatchedStyle(elem, property){
// element property has highest priority
var val = elem.style.getPropertyValue(property);
// if it's important, we are done
if(elem.style.getPropertyPriority(property))
return val;
// get matched rules
var rules = getMatchedCSSRules(elem);
// iterate the rules backwards
// rules are ordered by priority, highest last
for(var i = rules.length; i --> 0;){
var r = rules[i];
var important = r.style.getPropertyPriority(property);
// if set, only reset if important
if(val == null || important){
val = r.style.getPropertyValue(property);
// done if important
if(important)
break;
}
}
return val;
}
Example
Given the following code and style rules:
<div class="b">div 1</div>
<div id="a" class="a d2">div 2</div>
<div id="b" class="b d3" style="width: 333px;">div 3</div>
<div id="c" class="c d4" style="width: 44em;">div 4</div>
<style>
div { width: 100px; }
.d3 { width: auto !important; }
div#b { width: 80%; }
div#c.c { width: 444px; }
x, div.a { width: 50%; }
.a { width: 75%; }
</style>
this JS code
var d = document.querySelectorAll('div');
for(var i = 0; i < d.length; ++i){
console.log("div " + (i+1) + ": " + getMatchedStyle(d[i], 'width'));
}
gives the following widths for the divs:
div 1: 100px
div 2: 50%
div 3: auto
div 4: 44em
(At jsFiddle)
Good news everyone! There seems to be a CSS Typed OM on his way in the w3c drafts.
Fast reading this document, it seems that the goal of this maybe to-be specification, is to ease the access of CSSOM values from javascript.
The really important part of this for us here is that we will have a CSSUnitValue API, which will be able to parse CSS values to an object of the form
{
value: 100,
unit: "percent", // | "px" | "em" ...
type: "percent" // | "length"
}
And add a computedStyleMap() method, to the Element interface, from which we will be able to get the values actually applied on our elements.
As of today, only Chrome implements it (since 66).
(() => {
if (!Element.prototype.computedStyleMap) {
console.error("Your browser doesn't support CSS Typed OM");
return;
}
document.querySelectorAll('.test')
.forEach((elem) => {
let styleMap = elem.computedStyleMap();
const unitvalue = styleMap.get('width');
console.log(elem, {
type: unitvalue.type(),
unit: unitvalue.unit,
value: unitvalue.value
});
});
/* outputs
<div class="b test">first</div> {
"type": {
"length": 1
},
"unit": "px",
"value": 100
}
<div id="a" class="a test">second</div> {
"type": {
"percent": 1
},
"unit": "percent",
"value": 50
}
*/
})();
div.test { width: 100px; }
x,div#a { width: 50%; }
.a { width: 75%; }
<div class="b test">first</div>
<div id="a" class="a test">second</div>
Apparently there is no DOM API for this
https://developer.mozilla.org/en/DOM/window.getComputedStyle#Notes
EDIT: oops, just realized this was marked for Google Chrome
Try window.getMatchedCSSRules()
There's a newer duplicate post with a great answer here. That answer was for jQuery but it's easy to implement in pure js.
function getDefaultStyle(element, prop) {
var parent = element.parentNode,
computedStyle = getComputedStyle(element),
value;
parent.style.display = 'none';
value = computedStyle.getPropertyValue(prop);
parent.style.removeProperty('display');
return value;
}
I'm surprised not to see this answer, so: You can get there by going through the stylesheets yourself and getting the information about the rules that match the element.
Here's a rough sketch of an example, using the specificity library to calculate selector specificity. getComputedStyle would tell you those sizes in pixels rather than the original units.
function applyStyles(target, style, specificity, appliedSpecs) {
// Loop through its styles
for (let [key, value] of Object.entries(style)) {
// Skip the numerically-indexed ones giving us property names
if (/^\d+$/.test(key)) {
continue;
}
if (value !== "") {
// Non-blank style. If it has !important, add to specificity.
let thisSpec = specificity;
if (style.getPropertyPriority(key) === "important") {
// Important rule, so bump the first value (which will currently be 0
// for a stylesheet style and 1 for an inline style
thisSpec = [specificity[0] + 1, ...specificity.slice(1)];
}
// Non-blank style, do we have a style already and if so, with
// what specificity?
const currentSpec = appliedSpecs[key];
if (!currentSpec || SPECIFICITY.compare(thisSpec, currentSpec) >= 0) {
// Either we didn't already have this style or this new one
// has the same or higher specificity and overrides it.
target[key] = value;
appliedSpecs[key] = thisSpec;
}
}
}
}
function getDeclaredStyle(el) {
// An object to fill in with styles
const style = {};
// An object to remember the specificity of the selector that set a style
const appliedSpecs = {};
// Loop through the sheets in order
for (const sheet of Array.from(el.ownerDocument.styleSheets)) {
// Loop through the rules
const rules = sheet.cssRules || sheet.rules;
if (rules) {
for (const rule of Array.from(rules)) {
const {selectorText} = rule;
if (selectorText && el.matches(selectorText)) {
// This rule matches our element
if (rule.style) {
// Get the specificity of this rule
const specificity = SPECIFICITY.calculate(selectorText)[0].specificityArray;
// Apply these styles
applyStyles(style, rule.style, specificity, appliedSpecs);
}
}
}
}
}
// Apply inline styles
applyStyles(style, el.style, [0, 255, 255, 255], appliedSpecs);
return style;
}
// Get the element
const el = document.querySelector("div.a.b");
// Get its declared style
const style = getDeclaredStyle(el);
// Height is 3em because .a.b is more specific than .a
console.log("height: " + style.height); // "3em"
// Width is 5em because of the !important flag; it overrides the inline style rule
console.log("width: " + style.width); // "5em"
// Border width is 1em because the rule is later than the other rules
console.log("line-height: " + style.lineHeight); // "1.2"
// Color is blue because the inline style rule is !important
console.log("color: " + style.color); // "blue"
// Compare with results of `getComputedStyle`:
const computed = getComputedStyle(el);
console.log("computed height: " + computed.height);
console.log("computed width: " + computed.width);
console.log("computed line-height: " + computed.lineHeight);
console.log("completed color: " + computed.color);
.a {
width: 1em;
height: 1em;
width: 5em !important;
color: red !important;
line-height: 1.0;
color: yellow !important;
}
.a.b {
height: 3em;
}
.a {
height: 2em;
width: 4em;
line-height: 1.2;
}
.as-console-wrapper {
max-height: 100% !important;
}
<script src="//unpkg.com/specificity#0.4.1/dist/specificity.js"></script>
<div class="a b" style="width: 4em; color: blue !important">x</div>
Again, that's just a sketch, but it should head you the right way...
Here's an ES5 version:
// Get the element
var el = document.querySelector("div.a.b");
// An object to fill in with styles
var style = {};
// An object to remember the specificity of the selector that set a style
var specificity = {};
// Loop through the sheets in order
for (var sheetIndex = 0; sheetIndex < document.styleSheets.length; ++sheetIndex) {
var sheet = document.styleSheets[sheetIndex];
// Loop through the rules
var rules = sheet.cssRules || sheet.rules;
if (rules) {
for (var ruleIndex = 0; ruleIndex < rules.length; ++ruleIndex) {
var rule = rules[ruleIndex];
var selectorText = rule.selectorText;
if (selectorText && el.matches(selectorText)) {
// This rule matches our element
if (rule.style) {
// Get the specificity of this rule
var spec = SPECIFICITY.calculate(selectorText)[0].specificityArray;
// Loop through its styles
for (var key in rule.style) {
// Skip inherited ones and the numerically-indexed ones giving us property names
if (/^\d+$/.test(key) || !rule.style.hasOwnProperty(key)) {
continue;
}
var value = rule.style[key];
if (value !== "") {
// Non-blank style. If it has !important, add to specificity
var thisSpec = spec;
if (rule.style.getPropertyPriority(key) === "important") {
thisSpec = spec.slice();
thisSpec[0] = 1;
}
// Non-blank style, do we have a style already and if so, with
// what specificity?
var currentSpec = specificity[key];
if (!currentSpec || SPECIFICITY.compare(thisSpec, currentSpec) >= 0) {
// Either we didn't already have this style or this new one
// has the same or higher specificity and overrides it
style[key] = value;
specificity[key] = thisSpec;
}
}
}
}
}
}
}
}
// Height is 3em because .a.b is more specific than .a
console.log("height: " + style.height); // "3em"
// Width is 5em because of the !important flag
console.log("width: " + style.width); // "5em"
// Border width is 1em because the rule is later than the other rules
console.log("line-height: " + style.lineHeight); // "1.2"
// Compare with results of `getComputedStyle`:
var computed = getComputedStyle(el);
console.log("computed height: " + computed.height);
console.log("computed width: " + computed.width);
console.log("computed line-height: " + computed.lineHeight);
.a {
height: 1em;
width: 5em !important;
line-height: 1.0;
}
.a.b {
height: 3em;
}
.a {
height: 2em;
width: 4em;
line-height: 1.2;
}
<script src="//unpkg.com/specificity#0.4.1/dist/specificity.js"></script>
<div class="a b"></div>
Note: The two big things the above doesn't do are:
Handle styles inherited from an ancestor element. If you're interested in just a single property that you know is inherited, you could use the above and if it doesn't have the property set, repeat for the parent, etc. Or it's possible to extend this to apply inheritance based on the list of properties which says whether they're inherited or not and the rules of inheritance (being careful to allow for the inherit, initial, unset, and revert keywords, as well as the all keyword).
Media queries. The snippet above just applies all rules with styles. It should check for CSSMediaRules, see if they match the current media (probably using matchMedia), and if so go into their cssRules and apply them. Probably not all that hard.
<input type="text" value="1" style="min-width:1px;" />
This is my code and it is not working. Is there any other way in HTML, JavaScript, PHP or CSS to set minimum width?
I want a text input field with a dynamically changing width, so that the input field fluids around its contents. Every input has a built-in padding of 2em, that is the problem and second problem is that min-width ain't working on input at all.
If I set width more than it is needed than the whole program is messy, I need the width of 1px, more only if it's needed.
In modern browser versions, CSS unit ch is also available. To my understanding, it is font-independent unit, where 1ch equals to width of character 0 (zero) in any given font.
Thus, something as simple as following could be used as resize function, by binding to the input event:
var input = document.querySelector('input'); // get the input element
input.addEventListener('input', resizeInput); // bind the "resizeInput" callback on "input" event
resizeInput.call(input); // immediately call the function
function resizeInput() {
this.style.width = this.value.length + "ch";
}
input{ font-size:1.3em; padding:.5em; }
<label>Text
<input>
</label>
That example would resize the input to length of the value + 2 characters extra.
One potential problem with the unit ch is that in many fonts (i.e. Helvetica) the width of the character "m" exceeds the width of the character 0 and the character "i" is much narrower. 1ch is usually wider than the average character width, usually by around 20-30% according to this post.
It sounds like your expectation is that the style be applied dynamically to the width of the textbox based on the contents of the textbox. If so you will need some js to run on textbox contents changing, something like this:
<input id="txt" type="text" onkeypress="this.style.width = ((this.value.length + 1) * 8) + 'px';">
Note: this solution only works when every character is exactly 8px wide. You could use the CSS-Unit "ch" (characters) which represents the width of the character "0" in the chosen font. You can read about it here.
To calculate the width of the current input, you'll have to embed it in a temporary span element, attach that thing to the DOM, get the computed width (in pixels) using the scrollWidth property and remove the span again. Of course you'll have to ensure that the same font family, font size, etc., is used in the input as well as in the span element. Therefore I assigned the same class to them.
I attached the function to the keyup event, as on keypress the input character is not yet added to the input value, so that will result in the wrong width. Unfortunately, I don't know how to get rid of the scrolling of the input field (when adding characters to the end of the field); it scrolls, because the character is added and shown before adjustWidthOfInput() is called. And, as said, I can't do this the other way round because then you'll have the value of the input field before the pressed character is inserted. I'll try to solve this issue later.
BTW, I only tested this in Firefox (3.6.8), but you'll get the point, I hope.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Get/set width of <input></title>
<style>
body {
background: #666;
}
.input-element {
border: 0;
padding: 2px;
background: #fff;
font: 12pt sans-serif;
}
.tmp-element {
visibility: hidden;
white-space: pre;
}
</style>
</head>
<body>
<input id="theInput" type="text" class="input-element" value="1">
<script>
var inputEl = document.getElementById("theInput");
function getWidthOfInput() {
var tmp = document.createElement("span");
tmp.className = "input-element tmp-element";
tmp.innerHTML = inputEl.value.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
document.body.appendChild(tmp);
var theWidth = tmp.getBoundingClientRect().width;
document.body.removeChild(tmp);
return theWidth;
}
function adjustWidthOfInput() {
inputEl.style.width = getWidthOfInput() + "px";
}
adjustWidthOfInput();
inputEl.onkeyup = adjustWidthOfInput;
</script>
</body>
</html>
Here is a solution without monospaced font needed, with only a very small piece code of javascript, does not need to calculate computed styles, and even supports IME, supports RTL text.
// copy the text from input to the span
$(function () {
$('.input').on('input', function () { $('.text').text($('.input').val()); });
});
* {
box-sizing: border-box;
}
.container {
display: inline-block;
position: relative;
}
.input,
.text {
margin: 0;
padding: 2px 10px;
font-size: 24px;
line-height: 32px;
border: 1px solid #ccc;
box-radius: 3px;
height: 36px;
font: 20px/20px sans-serif;
/* font: they should use same font; */
}
.text {
padding-right: 20px;
display: inline-block;
visibility: hidden;
white-space: pre;
}
.input {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
}
<script src="https://code.jquery.com/jquery-3.2.0.min.js"></script>
<div class="container">
<span class="text">
some text
</span>
<input class="input" value="some text" />
</div>
Use the span.text to fit width of text, and let the input have same size with it by position: absolute to the container. Copy value of input to the span every time it changes (you may change this piece of code to vanilla JS easily, or use features provided by your frontend framework). So the input will just fit the size of its content.
Here's a modification of Lyth's answer that takes into account:
Deletion
Initialisation
Placeholders
It also allows for any number of input fields! To see it in action: http://jsfiddle.net/4Qsa8/
Script:
$(document).ready(function () {
var $inputs = $('.resizing-input');
// Resize based on text if text.length > 0
// Otherwise resize based on the placeholder
function resizeForText(text) {
var $this = $(this);
if (!text.trim()) {
text = $this.attr('placeholder').trim();
}
var $span = $this.parent().find('span');
$span.text(text);
var $inputSize = $span.width();
$this.css("width", $inputSize);
}
$inputs.find('input').keypress(function (e) {
if (e.which && e.charCode) {
var c = String.fromCharCode(e.keyCode | e.charCode);
var $this = $(this);
resizeForText.call($this, $this.val() + c);
}
});
// Backspace event only fires for keyup
$inputs.find('input').keyup(function (e) {
if (e.keyCode === 8 || e.keyCode === 46) {
resizeForText.call($(this), $(this).val());
}
});
$inputs.find('input').each(function () {
var $this = $(this);
resizeForText.call($this, $this.val())
});
});
Style:
.resizing-input input, .resizing-input span {
font-size: 12px;
font-family: Sans-serif;
white-space: pre;
padding: 5px;
}
HTML:
<div class="resizing-input">
<input type="text" placeholder="placeholder"/>
<span style="display:none"></span>
</div>
$(document).ready(function() {
var $inputs = $('.resizing-input');
// Resize based on text if text.length > 0
// Otherwise resize based on the placeholder
function resizeForText(text) {
var $this = $(this);
if (!text.trim()) {
text = $this.attr('placeholder').trim();
}
var $span = $this.parent().find('span');
$span.text(text);
var $inputSize = $span.width();
$this.css("width", $inputSize);
}
$inputs.find('input').keypress(function(e) {
if (e.which && e.charCode) {
var c = String.fromCharCode(e.keyCode | e.charCode);
var $this = $(this);
resizeForText.call($this, $this.val() + c);
}
});
// Backspace event only fires for keyup
$inputs.find('input').keyup(function(e) {
if (e.keyCode === 8 || e.keyCode === 46) {
resizeForText.call($(this), $(this).val());
}
});
$inputs.find('input').each(function() {
var $this = $(this);
resizeForText.call($this, $this.val())
});
});
.resizing-input input,
.resizing-input span {
font-size: 12px;
font-family: Sans-serif;
white-space: pre;
padding: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="resizing-input">
First:
<input type="text" placeholder="placeholder" />
<span style="display:none"></span>
</div>
<br>
This is an Angular-specific answer, but this worked for me and has been very satisfying in terms of its simplicity and ease-of-use:
<input [style.width.ch]="value.length" [(ngModel)]="value" />
It automatically updates via the character units in Jani's answer.
FOR A NICER LOOK&FEEL
You should use jQuery keypress() event in combination with String.fromCharCode(e.which) to get the pressed character. Hence you can calculate what your width will be. Why? Because it will look a lot more sexy :)
Here is a jsfiddle that results in a nice behaviour compared to solutions using the keyup event : http://jsfiddle.net/G4FKW/3/
Below is a vanilla JS which listens to the input event of an <input> element and sets a span sibling to have the same text value in order to measure it.
document.querySelector('input').addEventListener('input', onInput)
function onInput(){
var spanElm = this.nextElementSibling;
spanElm.textContent = this.value; // the hidden span takes the value of the input;
this.style.width = spanElm.offsetWidth + 'px'; // apply width of the span to the input
};
/* it's important the input and its span have same styling */
input, .measure {
padding: 5px;
font-size: 2.3rem;
font-family: Sans-serif;
white-space: pre; /* white-spaces will work effectively */
}
.measure{
position: absolute;
left: -9999px;
top: -9999px;
}
<input type="text" />
<span class='measure'></span>
Here is an alternative way to solve this using a DIV and the 'contenteditable' property:
HTML:
<div contenteditable = "true" class = "fluidInput" data-placeholder = ""></div>
CSS: (to give the DIV some dimensions and make it easier to see)
.fluidInput {
display : inline-block;
vertical-align : top;
min-width : 1em;
height : 1.5em;
font-family : Arial, Helvetica, sans-serif;
font-size : 0.8em;
line-height : 1.5em;
padding : 0px 2px 0px 2px;
border : 1px solid #aaa;
cursor : text;
}
.fluidInput * {
display : inline;
}
.fluidInput br {
display : none;
}
.fluidInput:empty:before {
content : attr(data-placeholder);
color : #ccc;
}
Note: If you are planning on using this inside of a FORM element that you plan to submit, you will need to use Javascript / jQuery to catch the submit event so that you can parse the 'value' ( .innerHTML or .html() respectively) of the DIV.
Just adding on top of other answers.
I noticed that nowadays in some browsers the input field has a scrollWidth. Which means:
this.style.width = this.scrollWidth + 'px';
should work nicely. tested in chrome, firefox and safari.
For deletion support, you can add '=0' first and then readjust.
this.style.width = 0; this.style.width = this.scrollWidth + 'px';
This answer provides one of the most accurate methods of retrieving text width available in the browser and is more accurate than the accepted answer. It uses the canvas html5 element and unlike other answers does not add the element into the DOM and thus avoids any reflow issues caused by excessively adding elements to the DOM.
Read more about the Canvas element here in relation to text width.
NOTE: According to MDN the shorthand versions of the getPropertyValue() method such as font can be unreliable. I'd recommend getting the values singularly to improve compatibility. I only used it here for speed.
/**
* returns the width of child text of any DOM node as a float
*/
function getTextWidth(el) {
// uses a cached canvas if available
var canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
var context = canvas.getContext("2d");
// get the full font style property
var font = window.getComputedStyle(el, null).getPropertyValue('font');
var text = el.value;
// set the font attr for the canvas text
context.font = font;
var textMeasurement = context.measureText(text);
return textMeasurement.width;
}
var input = document.getElementById('myInput');
// listen for any input on the input field
input.addEventListener('input', function(e) {
var width = Math.floor(getTextWidth(e.target));
// add 10 px to pad the input.
var widthInPx = (width + 10) + "px";
e.target.style.width = widthInPx;
}, false);
#myInput {
font: normal normal 400 normal 18px / normal Roboto, sans-serif;
min-width: 40px;
}
<input id="myInput" />
You can set an input's width using the size attribute as well. The size of an input determines it's width in characters.
An input could dynamically adjust it's size by listening for key events.
For example
$("input[type='text']").bind('keyup', function () {
$(this).attr("size", $(this).val().length );
});
JsFiddle here
You could do something like this
// HTML
<input id="input" type="text" style="width:3px" />
// jQuery
$(function(){
$('#input').keyup(function(){
$('<span id="width">').append( $(this).val() ).appendTo('body');
$(this).width( $('#width').width() + 2 );
$('#width').remove();
});
});
You can just set size attribute.
If you're using one of reactive frameworks, the following will be enough:
<input size="{{ yourValue.length }}" [value]="yourValue" />
but if you're using pure js, you should set event handlers, like:
<input oninput="this.setAttribute('size', this.value.length)" />
It's worth noting that a nice-looking resize can be done when the font is monospaced, so we can perfectly resize the input element using the ch unit.
Also in this approach we can update the width of the field by just updating a CSS variable (custom property) on input event and we should also take care of already pre-filled input on DOMContentLoaded event
Codepen demo
Markup
<input type="text" value="space mono font" class="selfadapt" />
CSS
:root { --size: 0; }
.selfadapt {
padding: 5px;
min-width: 10ch;
font-family: "space mono";
font-size: 1.5rem;
width: calc(var(--size) * 1ch);
}
As a root variable we set --size: 0: this variable will contain the length of the input and it will be multiplied by 1ch inside the calc() expression. By default we could also set a min-width, e.g. 10ch
The Javascript part reads the length of the value inserted and updates the variable --size:
JS
let input = document.querySelector('.selfadapt');
let root = document.documentElement.style;
/* on input event auto resize the field */
input.addEventListener('input', function() {
root.setProperty('--size', this.value.length );
});
/* resize the field if it is pre-populated */
document.addEventListener("DOMContentLoaded", function() {
root.setProperty('--size', input.value.length);
});
of course this still works even if you don't use monospaced font, but in that case you will need to change the calc() formula by multiplying the --size variable by another value (which it's strictly dependent on the font-family and font-size) different than 1ch.
Here is a plain JS and a jQuery plugin I wrote that will handle resizing an input element using a canvas and the font size / family to determine the actual string length when rendered. (only works in > IE9, chrome, safari, firefox, opera and most other major browsers that have implemented the canvas element).
PlainJS:
function autoSize(input, o) {
o || (o = {});
o.on || (o.on = 'keyup');
var canvas = document.createElement('canvas');
canvas.setAttribute('style', 'position: absolute; left: -9999px');
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
input.addEventListener(o.on, function () {
ctx.font = getComputedStyle(this,null).getPropertyValue('font');
this.style.width = ctx.measureText(this.value + ' ').width + 'px';
});
}
//Usage
autoSize(document.getElementById('my-input'));
jQuery Plugin:
$.fn.autoSize = function(o) {
o = $.extend({}, {
on: 'keyup'
}, o);
var $canvas = $('<canvas/>').css({position: 'absolute', left: -9999});
$('body').append($canvas);
var ctx = $canvas[0].getContext('2d');
return this.on(o.on, function(){
var $this = $(this);
ctx.font = $this.css('font');
$this.width(ctx.measureText($this.val()).width + 'px');
})
}
//Usage:
$('#my-input').autoSize();
Note: this will not handle text-transforms, line spacing and letter spacing, and probably some other text size changing properties. To handle text-transform property set and adjust the text value to match that property. The others are probably fairly straight forward. I will implement if this starts gaining some traction...
A bullet-proof, generic way has to:
Take into account all possible styles of the measured input element
Be able to apply the measurement on any input without modifying the HTML or
Codepen demo
var getInputValueWidth = (function(){
// https://stackoverflow.com/a/49982135/104380
function copyNodeStyle(sourceNode, targetNode) {
var computedStyle = window.getComputedStyle(sourceNode);
Array.from(computedStyle).forEach(key => targetNode.style.setProperty(key, computedStyle.getPropertyValue(key), computedStyle.getPropertyPriority(key)))
}
function createInputMeassureElm( inputelm ){
// create a dummy input element for measurements
var meassureElm = document.createElement('span');
// copy the read input's styles to the dummy input
copyNodeStyle(inputelm, meassureElm);
// set hard-coded styles needed for propper meassuring
meassureElm.style.width = 'auto';
meassureElm.style.position = 'absolute';
meassureElm.style.left = '-9999px';
meassureElm.style.top = '-9999px';
meassureElm.style.whiteSpace = 'pre';
meassureElm.textContent = inputelm.value || '';
// add the meassure element to the body
document.body.appendChild(meassureElm);
return meassureElm;
}
return function(){
return createInputMeassureElm(this).offsetWidth;
}
})();
// delegated event binding
document.body.addEventListener('input', onInputDelegate)
function onInputDelegate(e){
if( e.target.classList.contains('autoSize') )
e.target.style.width = getInputValueWidth.call(e.target) + 'px';
}
input{
font-size:1.3em;
padding:5px;
margin-bottom: 1em;
}
input.type2{
font-size: 2.5em;
letter-spacing: 4px;
font-style: italic;
}
<input class='autoSize' value="type something">
<br>
<input class='autoSize type2' value="here too">
Quite simple:
oninput='this.style.width = (this.scrollWidth - N) + "px";'
Where N is some number (2 in the example, 17 on something I'm developing) that is determined experimentally.
Subtracting N prevents this strange extrenuous space from accumulating long before the text reaches the end of the text box.
Compare. Pay careful attention to how the size changes after even just the first character.
<p>Subtracting N:</p>
<input type="text" placeholder="enter lots of text here" oninput='this.style.width = (this.scrollWidth-2) + "px";'>
<p>Not Subtracting N:</p>
<input type="text" placeholder="enter lots of text here" oninput='this.style.width = (this.scrollWidth) + "px";'>
Here is my React solution, it works with any font size, just make sure you have a monospace font (all font character widths are the same on monospace fonts) like i have in my solution, and it will work perfectly.
JS:
const [value, setValue] = useState(0)
HTML:
<input value={value} onChange={(e) => {setValue(e.target.value)}} style={{width: `${value.toString().length}`ch}}/>
CSS:
#import url("https://fonts.googleapis.com/css2?family=B612+Mono&display=swap");
input{
font-family: "B612 Mono", monospace;
}
You can do it even simpler in angularjs using the built-in ng-style directive.
In your html:
<input ng-style="inputStyle(testdata)" ng-model="testdata" />
In your controller:
$scope.testdata = "whatever";
$scope.inputStyle = function(str) {
var charWidth = 7;
return {"width": (str.length +1) * charWidth + "px" };
};
In your css:
input { font-family:monospace; font-size:12px; }
Adjust the charWidth to match the width of your font. It seems to be 7 at a font-size of 12px;
If you use Bootstrap, it could be done very easily:
<div contenteditable="true" class="form-control" style="display: inline"></div>
You will just need to fetch div's content and put it in a hidden input before submitting the form.
Here is my 2 cents.
Create an empty invisible div. Fill it with the input content and return the width to the input field. Match text styles between each box.
$(".answers_number").keyup(function(){
$( "#number_box" ).html( $( this ).val() );
$( this ).animate({
width: $( "#number_box" ).width()+20
}, 300, function() {
});
});
#number_box {
position: absolute;
visibility: hidden;
height: auto;
width: auto;
white-space: nowrap;
padding:0 4px;
/*Your font styles to match input*/
font-family:Arial;
font-size: 30px;
}
.answers_number {
font-size: 30px;
font-family:Arial;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="number" class="answers_number" />
<div id="number_box">
</div>
Here's a simple function to get what is needed:
function resizeInput() {
const input = document.getElementById('myInput');
input.style.width = `${input.scrollWidth}px`;
};
I think you're misinterpreting the min-width CSS property. min-width is generally used to define a minimum DOM width in a fluid layout, like:
input {
width: 30%;
min-width: 200px;
}
That would set the input element to a minimum width of 200 pixels. In this context, "px" stands for "pixels".
Now, if you're trying to check to make sure that input field contains at least one character when a user submits it, you'll need to do some form validation with JavaScript and PHP. If that is indeed what you're attempting to do, I'll edit this answer and do my best to help you out.
I really liked Lyth's answer, but also really wanted it to:
Handle backspace and delete
Not require you to manually add an adjacent tag.
Enforce a min width.
Automatically be applied to elements with a specific class
I adapted his JSFiddle and came up with this. One improvement not present in this fiddle would be to use something like the jQuery CSS Parser to actually read the initial width from the input.textbox-autosize rule, and use that as the minWidth. Right I'm simply using an attribute on the , which makes for a compact demo but is not ideal. as it requires an extra attribute on each input. You might also just want to put the minWidth as 100 right in the JavaScript.
HTML:
<div id='applicationHost'>
<div>Name: <input class='textbox-autosize' data-min-width='100' type="text" /></div>
<div>Email: <input class='textbox-autosize' data-min-width='100' type="email" /></div>
<div>Points: <input class='textbox-autosize' data-min-width='100' type="number" /></div>
</div>
CSS:
#applicationHost {
font-family: courier;
white-space: pre;
}
input.textbox-autosize, span.invisible-autosize-helper {
padding:0;
font-size:12px;
font-family:Sans-serif;
white-space:pre;
}
input.textbox-autosize {
width: 100px; /* Initial width of textboxes */
}
/*
In order for the measurements to work out, your input and the invisible
span need to have the same styling.
*/
JavaScript:
$('#applicationHost').on('keyup', '.textbox-autosize', function(e) {
// Add an arbitary buffer of 15 pixels.
var whitespaceBuffer = 15;
var je = $(this);
var minWidth = parseInt(je.attr('data-min-width'));
var newVal = je.val();
var sizingSpanClass = 'invisible-autosize-helper';
var $span = je.siblings('span.' + sizingSpanClass).first();
// If this element hasn't been created yet, we'll create it now.
if ($span.length === 0) {
$span = $('<span/>', {
'class': sizingSpanClass,
'style': 'display: none;'
});
je.parent().append($span);
}
$span = je.siblings('span').first();
$span.text(newVal) ; // the hidden span takes
// the value of the input
$inputSize = $span.width();
$inputSize += whitespaceBuffer;
if($inputSize > minWidth)
je.css("width", $inputSize) ; // apply width of the span to the input
else
je.css("width", minWidth) ; // Ensure we're at the min width
});
Better is onvalue:
<input id="txt" type="text" onvalue="this.style.width = ((this.value.length + 1) * 8) + 'px';">
It also involves pasting, dragging and dropping, etc.
The best solution is <input ... size={input.value.length} />
Svelte version:
<input type="text" style="width: {tag.length}ch" bind:value={tag} />
I just spend some time figuring out how to do it.
Actually the simplest way I found is to move input value to span just before the input, keeping input 1 symbol width. Though I can't be sure that it fit for your initial need.
Maybe it some extra code, but in react+flux based application it is quite natural solution.
Based off Michael's answer, I have created my own version of this using jQuery. I think it is a cleaner/shorter version of most answers here and it seems to get the job done.
I am doing the same thing as most of the people here by using a span to write the input text into then getting the width. Then I am setting the width when the actions keyup and blur are called.
Here is a working codepen. This codepen shows how this can be used with multiple input fields.
HTML Structure:
<input type="text" class="plain-field" placeholder="Full Name">
<span style="display: none;"></span>
jQuery:
function resizeInputs($text) {
var text = $text.val().replace(/\s+/g, ' '),
placeholder = $text.attr('placeholder'),
span = $text.next('span');
span.text(placeholder);
var width = span.width();
if(text !== '') {
span.text(text);
var width = span.width();
}
$text.css('width', width + 5);
};
The function above gets the inputs value, trims the extra spaces and sets the text into the span to get the width. If there is no text, it instead gets the placeholder and enters that into the span instead. Once it enters the text into the span it then sets the width of the input. The + 5 on the width is because without that the input gets cut off a tiny bit in the Edge Browser.
$('.plain-field').each(function() {
var $text = $(this);
resizeInputs($text);
});
$('.plain-field').on('keyup blur', function() {
var $text = $(this);
resizeInputs($text);
});
$('.plain-field').on('blur', function() {
var $text = $(this).val().replace(/\s+/g, ' ');
$(this).val($text);
});
If this could be improved please let me know as this is the cleanest solution I could come up with.
You would like to change the size attribute as the text changes.
# react
const resizeInput = (e) => {
e.target.setAttribute('size', e.target.value.length || 1);
}
<input
onChange={resizeInput}
size={(propertyInput.current && propertyInput.current.value.length) || 1}
ref={propertyInput}
/>