How to auto resize the textarea to fit the content? - javascript

I'm trying to auto resize the textarea so it fits the content in it but I'm facing a stuttering issue after I click enter to proceed to the next line. How can I fix this?
This is what I'm trying to do, see the image below.
Please see this link for the StackBlitz example
CODE
this.form.valueChanges.subscribe(() => {
const textarea = this.myDiv.nativeElement;
textarea.addEventListener('keydown', function() {
setTimeout(() => {
this.style.cssText = 'height:auto; padding:0';
this.style.cssText = 'height:' + this.scrollHeight + 'px';
}, 0);
});
});

addEventListener here is redundant since valueChanges already notifies you when the field changes. Instead, update the height using the ViewChild reference myDiv.
this.myForm.valueChanges.subscribe(value => {
this.myDiv.nativeElement.style.height = 'auto';
this.myDiv.nativeElement.style.height = `${this.myDiv.nativeElement.scrollHeight}px`;
});
Then add overflow: hidden to your css so the scrollbar doesn't show.
textarea {
resize: horizontal;
overflow: hidden;
}
You can keep the resize: horizontal; but it is no longer required since the textarea will resize automatically anyway.
Here is a working example on StackBlitz.

For anyone still looking for an answer to this in almost 2021, it's covered in the official Angular Material docs here. Directly manipulating the DOM via nativeElement is an anti-pattern.
<mat-form-field [style.fontSize]="fontSize.value">
<mat-label>Autosize textarea</mat-label>
<textarea matInput
cdkTextareaAutosize
#autosize="cdkTextareaAutosize"
cdkAutosizeMinRows="1"
cdkAutosizeMaxRows="5"></textarea>
</mat-form-field>

What you are trying to achieve is a very old trick. I have used it myself but trying a different approach.
It makes more sense why the text area is jumpy coz every keystroke you were making the height = 0 to calculate scroll height so that you can assign a new height.
I calculated the fontSize or lineHeight and calculated number of lines and the initial height to adjust based on that. So on every keystroke you are just assigning height w/o making the text area height=0
textareaProps = null;
getHeight(element) {
const lines = element.value.split(/\r\n|\r|\n/).length;
if(!this.textareaProps) {
const autoStyle = getComputedStyle(element);
const lineHeight = parseInt(autoStyle.lineHeight);
const adjust = parseInt(autoStyle.height) - lineHeight;
this.textareaProps = {adjust, lineHeight}
}
const { adjust, lineHeight } = this.textareaProps;
const height = lines * lineHeight + adjust;
return height + 'px';
}
You now need to call this method to get height and pass the textarea element as arg.
element.style.cssText = 'height:' + getHeight(element) ;
Edit 2
Sadly the above solution will only work if there are line breaks by user. When you enter a huge line text area wraps it but it doesn't increase the height. So intruducing a proxy html element which will have the text as same as text area value and will provide a height that we can assign to our text area.
textareaProps = null;
getHeight(element) {
if(!this.textareaProps) {
const proxy = document.createElement('div');
const {padding, width, fontSize, height, lineHeight} = getComputedStyle(element);
const css = [
'position:absolute',
'visibility: hidden',
'pointer-events:none',
`width: ${width}`,
`padding:${padding}`,
`min-height: ${height}`,
`font-size:${fontSize}`,
`line-height:${lineHeight}`,
].join(';');
proxy.style.cssText=css;
this.textareaProps = {
proxy: document.body.appendChild(proxy),
adjust: (parseInt(fontSize))
};
}
const { proxy, adjust} = this.textareaProps;
proxy.innerText = element.value + '.';
return (proxy.offsetHeight + adjust) + 'px';
}
Updated StackBlitz https://stackblitz.com/edit/next-line-view-child-ssnp4q

<textarea rows="15" id="code" disabled placeholder="Description"></textarea>
rows="15" to set min heigth to 15 rows
el = document.getElementById("code");
el.style.height = "auto";
// code to populate textarea
el.style.height = (5 + el.scrollHeight) + "px";
this helped me to set textarea to height as per content populated in it.

If you dont want a plugin there is a very simple solution
$(document).ready(function() {
$('textarea').on('keyup keypress', function() {
$(this).height(0);
$(this).height(this.scrollHeight);
});
$("textarea").each(function(textarea) {
$(this).height(0);
$(this).height(this.scrollHeight);
});
});

Related

Get the length of the text in an input in pixels [duplicate]

I have a input type text
<input type="text" id="txtid">
When i start typing text inside input, i should be able to get the lenght of the entered text.
This is what I tried:
document.getElementById("txtid").offsetWidth;
and
var test = document.getElementById("txtid");
var width = (test.clientWidth + 1) + "px";
These two does not give the width of the text entered
Basically what I want is:
For suppose input text width is 320px. I need the width of the entered text i.e 120px, which keeps on changing when I enter.
I see two ways.
First:
You can use a div with content editable instead input. Like this you can see the width of the div.
var elemDiv = document.getElementById('a');
elemDiv.onblur = function() {
console.log(elemDiv.clientWidth + 'px');
}
div {
width: auto;
display: inline-block;
}
<div id='a' contenteditable="plaintext-only">Test</div>
Note : Like #Leon Adler say, this way allows pasting images, tables and formatting from other programs. So you maybe need some validation with javascript to check the content before get the size.
Second:
Use an input type text and paste the content into an invisible div. And you can see the width of the invisible div.
var elemDiv = document.getElementById('a'),
elemInput = document.getElementById('b');
elemInput.oninput = function() {
elemDiv.innerText = elemInput.value;
console.log(elemDiv.clientWidth + 'px');
}
.div {
width: auto;
display: inline-block;
visibility: hidden;
position: fixed;
overflow:auto;
}
<input id='b' type='text'>
<div id='a' class='div'></div>
Note : For this way, you must have the same font and font size on input and div tags.
I have modified Chillers answer slightly, because it looks like you wanted the width rather than the letter count. I have created a span, which is absolute positioned off the screen. I am then adding the value of the input to it and then getting the width of the span. To make it more fancy you could create the span with javascript.
Note that the input and the span would have to have the same CSS styling for this to be accurate.
document.getElementById("txtid").addEventListener("keyup", function(){
var mrspan = document.getElementById("mrspan");
mrspan.innerText = this.value;
console.log(mrspan.offsetWidth + "px");
});
<input type="text" id="txtid">
<span id="mrspan" style="position:absolute;left:-100%;"></span>
Canvas measureText() method can be helpful in such a case. Call the following function whenever you need to get your text width:
function measureMyInputText() {
var input = document.getElementById("txtid");
var c = document.createElement("canvas");
var ctx = c.getContext("2d");
var txtWidth = ctx.measureText(input.value).width;
return txtWidth;
}
For more accurate result, you may set font styling to the canvas, especially if you set some font properties to the input. Use the following function to get the input font:
function font(element) {
var prop = ["font-style", "font-variant", "font-weight", "font-size", "font-family"];
var font = "";
for (var x in prop)
font += window.getComputedStyle(element, null).getPropertyValue(prop[x]) + " ";
return font;
}
Then, add this line to the frist function:
ctx.font = font(input);
UPDATE
Two things I want to put forward
Doing display none will not give you any offsetWidth if we are trying using vanilla JS. we can use visibility: hidden; or
opacity:0 for that.
we need to add overflow:auto to the hidden span or else the text will be warped to the next line if it exceeds the browser window
width and the width will stay fixed (width of window)
OLD
You can try this approach
Add a span and hide it
<span id="value"></span>
and then onkeyup add the text of the textfield on your hidden span and get its width.
WITH THE HELP OF JQUERY
SNIPPET (UPDATED)
function update(elm,value) {
$('#value').text(value);
var width = $('#value').width();
$('#result').text('The width of '+ value +' is '+width +'px');
}
#value{
display:none;
overflow:auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" oninput='update(this,this.value)'>
<span id="value"></span>
<div id="result"></div>
USING JS ONLY
SNIPPET (UPDATED)
function update(elm,value) {
var span = document.getElementById('value');
span.innerHTML = value;
var width = span.offsetWidth;
document.getElementById('result').innerHTML = 'The width of '+ value +' is '+ width+ 'px';
}
#value{
opacity:0;
overflow:auto;
}
<input type="text" oninput='update(this,this.value)'>
<span id="value"></span>
<div id="result"></div>
Using the above solution, I turned it into a single function.
let getWidth = (fontSize, value) => {
let div = document.createElement('div');
div.innerText = value;
div.style.fontSize = fontSize;
div.style.width = 'auto';
div.style.display = 'inline-block';
div.style.visibility = 'hidden';
div.style.position = 'fixed';
div.style.overflow = 'auto';
document.body.append(div)
let width = div.clientWidth;
div.remove();
return width;
};
getWidth('10px', 'test');

Get input text width when typing

I have a input type text
<input type="text" id="txtid">
When i start typing text inside input, i should be able to get the lenght of the entered text.
This is what I tried:
document.getElementById("txtid").offsetWidth;
and
var test = document.getElementById("txtid");
var width = (test.clientWidth + 1) + "px";
These two does not give the width of the text entered
Basically what I want is:
For suppose input text width is 320px. I need the width of the entered text i.e 120px, which keeps on changing when I enter.
I see two ways.
First:
You can use a div with content editable instead input. Like this you can see the width of the div.
var elemDiv = document.getElementById('a');
elemDiv.onblur = function() {
console.log(elemDiv.clientWidth + 'px');
}
div {
width: auto;
display: inline-block;
}
<div id='a' contenteditable="plaintext-only">Test</div>
Note : Like #Leon Adler say, this way allows pasting images, tables and formatting from other programs. So you maybe need some validation with javascript to check the content before get the size.
Second:
Use an input type text and paste the content into an invisible div. And you can see the width of the invisible div.
var elemDiv = document.getElementById('a'),
elemInput = document.getElementById('b');
elemInput.oninput = function() {
elemDiv.innerText = elemInput.value;
console.log(elemDiv.clientWidth + 'px');
}
.div {
width: auto;
display: inline-block;
visibility: hidden;
position: fixed;
overflow:auto;
}
<input id='b' type='text'>
<div id='a' class='div'></div>
Note : For this way, you must have the same font and font size on input and div tags.
I have modified Chillers answer slightly, because it looks like you wanted the width rather than the letter count. I have created a span, which is absolute positioned off the screen. I am then adding the value of the input to it and then getting the width of the span. To make it more fancy you could create the span with javascript.
Note that the input and the span would have to have the same CSS styling for this to be accurate.
document.getElementById("txtid").addEventListener("keyup", function(){
var mrspan = document.getElementById("mrspan");
mrspan.innerText = this.value;
console.log(mrspan.offsetWidth + "px");
});
<input type="text" id="txtid">
<span id="mrspan" style="position:absolute;left:-100%;"></span>
Canvas measureText() method can be helpful in such a case. Call the following function whenever you need to get your text width:
function measureMyInputText() {
var input = document.getElementById("txtid");
var c = document.createElement("canvas");
var ctx = c.getContext("2d");
var txtWidth = ctx.measureText(input.value).width;
return txtWidth;
}
For more accurate result, you may set font styling to the canvas, especially if you set some font properties to the input. Use the following function to get the input font:
function font(element) {
var prop = ["font-style", "font-variant", "font-weight", "font-size", "font-family"];
var font = "";
for (var x in prop)
font += window.getComputedStyle(element, null).getPropertyValue(prop[x]) + " ";
return font;
}
Then, add this line to the frist function:
ctx.font = font(input);
UPDATE
Two things I want to put forward
Doing display none will not give you any offsetWidth if we are trying using vanilla JS. we can use visibility: hidden; or
opacity:0 for that.
we need to add overflow:auto to the hidden span or else the text will be warped to the next line if it exceeds the browser window
width and the width will stay fixed (width of window)
OLD
You can try this approach
Add a span and hide it
<span id="value"></span>
and then onkeyup add the text of the textfield on your hidden span and get its width.
WITH THE HELP OF JQUERY
SNIPPET (UPDATED)
function update(elm,value) {
$('#value').text(value);
var width = $('#value').width();
$('#result').text('The width of '+ value +' is '+width +'px');
}
#value{
display:none;
overflow:auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" oninput='update(this,this.value)'>
<span id="value"></span>
<div id="result"></div>
USING JS ONLY
SNIPPET (UPDATED)
function update(elm,value) {
var span = document.getElementById('value');
span.innerHTML = value;
var width = span.offsetWidth;
document.getElementById('result').innerHTML = 'The width of '+ value +' is '+ width+ 'px';
}
#value{
opacity:0;
overflow:auto;
}
<input type="text" oninput='update(this,this.value)'>
<span id="value"></span>
<div id="result"></div>
Using the above solution, I turned it into a single function.
let getWidth = (fontSize, value) => {
let div = document.createElement('div');
div.innerText = value;
div.style.fontSize = fontSize;
div.style.width = 'auto';
div.style.display = 'inline-block';
div.style.visibility = 'hidden';
div.style.position = 'fixed';
div.style.overflow = 'auto';
document.body.append(div)
let width = div.clientWidth;
div.remove();
return width;
};
getWidth('10px', 'test');

How to refresh offsetHeight of div after changing img src on child using pure javascript?

I need to change an image in a div and then immediately centre it on the screen by calculating its height and width and then setting its left and top values. But when I change the img src and then try to get the div's offsetHeight and clientHeight, both the values are wrong. Strangely the offsetWidth and clientWidth values are correct.
Is there a way to refresh the div and get the correct value?
Edit: It appears that changing the image src makes everything after that not work. The change to the onclick event imageObj.onclick = function() {contractImageView(imageId);}; isn't working now either. If I comment out the if statement it all starts working again.
Below is the code...
// Get the page dimensions
var pageBody = document.getElementById(currentBody);
// If you couldn't get the page body, abort.
if (!pageBody) {
return;
}
var pageBodyHeight = window.innerHeight;
var pageBodyWidth = pageBody.offsetWidth;
var imageDiv = document.getElementById(imageId);
var imageObj = imageDiv.children[0];
var paraObj = imageDiv.children[1];
// If you can't get the div or its image, then abort.
if (!imageDiv || !imageObj) {
return;
}
// Check whether the image has been loaded yet, and load if needed
if (imageObj.src.indexOf('lazy_placeholder.gif') !== -1) {
for (item in photoLazyData) {
if (item === imageDiv.parentElement.id) {
imageObj.src = photoLazyData[item][imageDiv.id];
}
}
}
// Change the images class.
imageDiv.className = 'active_design_div';
imageDiv.style.visibility = 'visible';
imageDiv.style.opacity = 1;
// Set the objects new onclick method to collapse the image
imageObj.onclick = function() {contractImageView(imageId);};
// Calculate the right size
imageDiv.style.maxHeight = pageBodyHeight + 'px';
imageDiv.style.maxWidth = pageBodyWidth + 'px';
imageObj.style.maxHeight = pageBodyHeight + 'px';
imageObj.style.maxWidth = pageBodyWidth + 'px';
// Calculate the margins.
var imageDivWidth = imageDiv.offsetWidth || imageDiv.clientWidth;
// ## THIS IS WRONG IF THE ABOVE IF STATEMENT CHANGES THE SRC ##
var imageDivHeight = imageDiv.offsetHeight || imageDiv.clientHeight;
console.log(imageDiv.offsetHeight + ' : ' + imageDiv.clientHeight);
console.log(imageDiv.offsetWidth + ' : ' + imageDiv.clientWidth);
var leftOffset = (pageBodyWidth - imageDivWidth) / 2;
var topOffset = (pageBodyHeight - imageDivHeight) /2;
// Adjust styling to make the div visible and centred.
imageDiv.style.left = String(leftOffset) + 'px';
imageDiv.style.top = String(topOffset) + 'px';
currentId = imageId;
toggleBlackDiv();
Edit: I got this working in the end using Joonas89's answer. I basically moved the if statement that changes the src value to another function that is called prior to the one above. This solved the onclick problem mentioned above. I then changed the divs left/top from calculated values to 50% as detailed by Joonas89 (but on the container div rather than the img element), along with the new transform property. The div and img already had a position value of fixed so didn't need to change that.
Are you sure u want to calculate top and left positions? Wouldent it be better to add a class like this, that always centers it
.parentDiv{
position: relative;
}
.imageElement{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

How to set the font-size to 100% the size of a div?

I have a div with a static size. Sometimes longer text than the div will be placed there. Is there anyway to achieve the text fitting the div width at all times through JavaScript alone? I am aware there are jQuery fixes but need a pure JS solution in this case.
Even if you have a link to a demo/tutorial that would be helpful, thanks.
Here you go, this should do what you want: JSFiddle
Basically the key here is to check the output.scrollHeight against output.height. Consider the following setup:
HTML:
<button onclick="addText(); resizeFont()">Click Me to Add Some Text!</button>
<div id="output"></div>
CSS:
div {
width: 160px;
height: 160px;
}
This creates a square div and fills it with a randomly long string of text via the addText() method.
function addText() {
var text = "Lorem ipsum dolor amit",
len = Math.floor(Math.random() * 15),
output = document.querySelector('#output'),
str = [];
for (; --len;)
str.push(text);
output.innerHTML = str.join(', ');
}
The magic lies in the resizeFont() function. Basically what this does is, once the text has been added, it sets the fontSize of the div to be equal to its own height. This is the base case for when you have a string of 1 character (i.e. the fontSize will equal the height). As the length of your string grows, the fontSize will need to be made smaller until the scrollHeight equals the height of the div
function resizeFont() {
var output = document.querySelector('#output'),
numRE = /(\d+)px/,
height = numRE.exec(window.getComputedStyle(output).height)[1],
fontSize = height;
// allow div to be empty without entering infinite loop
if (!output.innerHTML.length) return;
// set the initial font size to the height of the div
output.style.fontSize = fontSize + 'px';
// decrease the font size until the scrollHeight == height
while (output.scrollHeight > height)
output.style.fontSize = --fontSize + 'px';
}
The nice thing about this method is that you can easily attach an event listener to the resize event of the window, making the text dynamically resize as the user changes the window size: http://jsfiddle.net/QvDy8/2/
window.onload = function() {
window.addEventListener('resize', resizeFont, false);
}

Change tinyMce editor's height dynamically

I am using tinymce editor in my page. What I want to do is to change the height of the editor dynamically. I have created a function:
function setComposeTextareaHeight()
{
$("#compose").height(200);
}
but that is not working.
My textarea is
<textarea id="compose" cols="80" name="composeMailContent" style="width: 100%; height: 100%">
I have tried all sorts of methods for changing the height but could not come to a resolution. Is there any thing that i am missing?
You can resize tinymce with the resizeTo theme method:
editorinstance.theme.resizeTo (width, height);
The width and height set the new size of the editing area - I have not found a way to deduce the extra size of the editor instance, so you might want to do something like this:
editorinstance.theme.resizeTo (new_width - 2, new_height - 32);
Try:
tinyMCE.init({
mode : "exact",
elements : "elm1",
....
To change size dynamically in your javascript code:
var resizeHeight = 350;
var resizeWidth = 450;
tinyMCE.DOM.setStyle(tinyMCE.DOM.get("elm1" + '_ifr'), 'height', resizeHeight + 'px');
tinyMCE.DOM.setStyle(tinyMCE.DOM.get("elm1" + '_ifr'), 'width', resizeWidth + 'px');
The following comes in from this other SO answer I posted:
None of the above were working for me in TinyMCE v4, so my solution was to calculate the height based on the toolbars/menu bar/status bar, and then set the height of the editor, taking those heights into consideration.
function resizeEditor(myHeight) {
window.console.log('resizeEditor');
myEditor = getEditor();
if (myEditor) {
try {
if (!myHeight) {
var targetHeight = window.innerHeight; // Change this to the height of your wrapper element
var mce_bars_height = 0;
$('.mce-toolbar, .mce-statusbar, .mce-menubar').each(function(){
mce_bars_height += $(this).height();
});
window.console.log('mce bars height total: '+mce_bars_height);
myHeight = targetHeight - mce_bars_height - 8; // the extra 8 is for margin added between the toolbars
}
window.console.log('resizeEditor: ', myHeight);
myEditor.theme.resizeTo('100%', myHeight); // sets the dimensions of the editable area
}
catch (err) {
}
}
}
In my case, I wanted the editor window to match the width and height of the actual window, since the editor would come up in a popup. To detect changes and resize, I set this to a callback:
window.onresize = function() {
resizeEditor();
}
It's a bit late but for Googler like me, check the autoresize plugin
tinymce.init({
plugins: "autoresize"
});
Options
autoresize_min_height : Min height value of the editor when it auto resizes.
autoresize_max_height : Max height value of the editor when it auto resizes.
I'm using tinymce 4.8.3.
I display the editor in a resizable modal dialog box.
I solved this using flexbox, shown here in SASS/SCSS:
// TinyMCE editor is inside a container something like this
.html-container {
height: 100%;
overflow: hidden;
}
.mce-tinymce {
// This prevents the bottom border being clipped
// May work with just 100%, I may have interference with other styles
height: calc(100% - 2px);
& > .mce-container-body {
height: 100%;
display: flex;
flex-direction: column;
& > .mce-edit-area {
flex: 1;
// This somehow prevents minimum height of iframe in Chrome
// If we resize too small the iframe stops shrinking.
height: 1px;
}
}
}
When the editor is initialized we have to tell it to put 100% height on the IFRAME. In my case I also have to subtract 2px else the right border is clipped off:
tinymce.init({
...
height: "100%",
width: "calc(100% - 2px)"
});
What ManseUK stated is almost correct.
The correct solution is:
$('#compose_ifr').height(200);
or in your case
$('#composeMailContent_ifr').height(200);
Update: maybe this is more what you are looking for:
// resizes editoriframe
resizeIframe: function(frameid) {
var frameid = frameid ? frameid : this.editor.id+'_ifr';
var currentfr=document.getElementById(frameid);
if (currentfr && !window.opera){
currentfr.style.display="block";
if (currentfr.contentDocument && currentfr.contentDocument.body.offsetHeight) { //ns6 syntax
currentfr.height = 200 + 26;
}
else if (currentfr.Document && currentfr.Document.body.scrollHeight) { //ie5+ syntax
currentfr.height = 200;
}
styles = currentfr.getAttribute('style').split(';');
for (var i=0; i<styles.length; i++) {
if ( styles[i].search('height:') ==1 ){
styles.splice(i,1);
break;
}
};
currentfr.setAttribute('style', styles.join(';'));
}
},
In case someone finds this and also wants to change the height of the source code editor plugin.
You need to edit the following file:
\tiny_mce\plugins\code\plugin.min.js
Look out for the attribute called minHeigh and adjust it to your needs. The height you define there is not the height of the entire box, but it is not the height of the textarea either. It is something inbetween.
You can set it according to your height
tinymce.init({
height: "500px"
});
I test this solution on version 5 of TinyMCE.
For change the height of TinyMCE after page loaded, all you need is: your element id
$(function(){
var selectedElement = tinymce.get('Element id without sharp(#)');
selectedElement.settings.height = 700;
selectedElement.settings.max_height = 400;
selectedElement.settings.min_height = 1000;
});
Also you can use autoresize plugin for get better experience:
tinymce.init({
plugins: 'wordcount autoresize',
})
$(window).load(function () {
$('#YourID_ifr').css('height', '550px');
});

Categories

Resources