Issues with textarea's scrollheight increasing inconsistently - javascript

This issue is not very easy to explain so I apologize if this question seems confusing.
Basically, I have a <textarea> who's height changes based off its value. If there is any vertical overflow (which would normally produce a vertical scrollbar), I increase the height of the <textarea> to match its scrollHeight property. This seems to work just fine for the first two lines, but when more text is added, I noticed that the point at which the scrollHeight increases is different for each line of text.
Here is a fiddle that demonstrates the strange behavior: http://jsfiddle.net/2zpkf6fL/2/
Type about 5 or 6 lines of text and you will see what I'm talking about.
Can anyone shed some light on this issue? Why does the scrollHeight increase at different points for different lines of text?

Here is how i do what you are looking to do.
HTML:
<div class="textarea-container">
<textarea></textarea>
<div class="textarea-size"></div>
</div>
CSS:
.textarea-container {
position: relative;
/* you should change this*/
width: 50%;
}
textarea, .textarea-size {
min-height: 25px;
/* need to manually set font and font size */
font-family: sans-serif;
font-size: 14px;
box-sizing: border-box;
padding: 4px;
border: 1px solid;
overflow: hidden;
width: 100%;
}
textarea {
height: 100%;
position: absolute;
resize:none;
/*
"pre" or "preline" or "normal" fixes Chrome issue where
whitespace at end of lines does not trigger a line break.
However, it causes the text to exhibit the behavior seen with
"pre" that is described below.
*/
white-space: normal;
}
.textarea-size {
visibility: hidden;
/*
Pre-wrap: preserve spacing and newlines, but wrap text.
Pre: preserve spacing and newlines but don't wrap text.
"pre" does not wrap well on Firefox, even with word-wrap:break-word.
"pre" on Chrome works with word-wrap, but exhibits different behavior:
Instead of entire words being moved to the next line for wrapping,
the browser will cut words in the middle for wrapping.
"pre-line" has Firefox issues
*/
white-space: pre-wrap;
/* Required for wrapping lines in Webkit,
but not necessary in Firefox if you have white-space wrapping
(pre-wrap, normal, pre-line) already set */
word-wrap: break-word;
overflow-wrap: break-word;
}
SCRIPT:
var textContainer, textareaSize, input;
var autoSize = function () {
textareaSize.innerHTML = input.value + '\n';
};
document.addEventListener('DOMContentLoaded', function() {
textContainer = document.querySelector('.textarea-container');
textareaSize = textContainer.querySelector('.textarea-size');
input = textContainer.querySelector('textarea');
autoSize();
input.addEventListener('input', autoSize);
});
Here is jsfiddle

Related

Identical elements have different scrollHeights when overflowed. What is the cause, and how can I fix it?

I have a plugin that creates and, on an interval, populates a <p> with the content of a <textarea>. The plugin positions the <p> underneath the <textarea>, and styles the elements so that their "boxes" are identical. Additionally, the background and text of the <textarea> are defined as transparent so that the content of the <p> can be seen.
Ideally, the elements and their contents will mirror one another at all times. And in most cases, they do. However, when both elements are made to be scrollable, the dynamic breaks; this is due to a difference in the scrollHeight of the two elements (the scrollHeight of <textArea> is larger than that of the <p>)
Here is the code:
var $shadowParagraphObj = $("#shadowParagraph");
var $contentTextAreaObj = $("#contentTextArea").scroll(scrollShadowParagraph);
function scrollShadowParagraph(event)
{
var textAreaScrollLeft = $contentTextAreaObj.scrollLeft();
var textAreaScrollTop = $contentTextAreaObj.scrollTop();
if($shadowParagraphObj.scrollLeft() != textAreaScrollLeft)
$shadowParagraphObj.scrollLeft(textAreaScrollLeft)
if($shadowParagraphObj.scrollTop() != textAreaScrollTop)
$shadowParagraphObj.scrollTop(textAreaScrollTop)
}
var intervalId = setInterval(function(){$shadowParagraphObj.html($contentTextAreaObj.val())}, 100);
#containerDiv {
position: relative;
left: 50%;
margin-left: -250px;
width: 510px;
height: 200px;
}
#shadowParagraph, #contentTextArea {
width: inherit;
height: inherit;
overflow: scroll !important;
padding: 4px;
border : none;
outline: none;
margin: 0px;
white-space: pre-wrap;
word-wrap: pre-wrap;
font: 1em Arial, sans-serif;
}
#shadowParagraph {
position: absolute;
z-index: 0;
background: white;
color: blue;
}
#contentTextArea {
position: relative;
z-index: 1;
background: transparent;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='containerDiv'>
<p id='shadowParagraph'></p>
<textarea id='contentTextArea'></textarea>
</div>
Overflowing the <textarea> should produce the issue (the text of the <textarea> has been given color to make the issue easy to see).
Have I forgot to declare some properties that are causing this discrepancy between scrollHeight values? If so, what are they and how should I declare them? If not, is there any way to ensure that the scrollHeight of the two elements is equal at all times?
Okay so using .replace(/\n\r?g, '<br />') to convert updated values, your line breaks will be converted into html line breaks. Additionally, html tends to ignore lone <br /> line breaks, so you will want to add an additional <br /> to the value to ensure the last line break is rendered.
Put together this would look something like:
var textAreaHTML = $myTextArea.val().replace(/\n\r?g, '<br />')+'<br />';
Additionally, I would recommend updating your textarea values AND scroll position on the .keyup() event, .keypress() event, or both events using .on('keyup keypress', function() {...}).
To see this in action check out this jsFiddle example

Get dimensions of text block via JavaScript, not the size of container's `getBoundingClientRect`

I want to get the size of text inside a container. Let's consider general case when the container has padding and border.
The problem is that getBoundingClientRect returns the size of text PLUS left border and padding, in case the text overflows. Otherwise it returns just the size of border box of the container.
You can get the width if you create a placeholder div with all of the same text formatting options and find it's width.
For instance, I will create a div with the class .hidden that has the same attributes as the original div.
div.container
{
font-size: 16px;
}
div.hidden
{
font-size: 16px;
display: none;
}
Then, using jQuery, copy the contents of .container to .hidden and find the width of .hidden:
$(function(){
$("div.container").each(function(){
$("body").append("<div class='hidden'>"+$(this).html()+"</div>");
var width = $("div.hidden").width();
$("div.width").html("Actual width: "+width+"px");
$("div.hidden").remove();
});
});
JSFiddle
Interesting! You could use javascript to clone the text inside of an empty element offscreen that has 0 padding/margin/border. Then you could get the width of that element.
var txt = document.getElementById('fixed').innerHTML,
clone = document.getElementById('clone');
clone.innerHTML = txt;
var width = clone.offsetWidth;
document.getElementById('output').innerHTML = width;
#fixed {
width: 8em;
height: 8em;
border: .5em solid red;
}
#clone {
margin: 0;
padding: 0;
border: 0;
position: fixed;
left: -9999px;
}
<div id="fixed">asdfkjahsdflkahjsdflkjhasdljfhalsdkjfhalsdkjfhalsdkjfhalksdhjflasd</div>
<div id="clone"></div>
Width of text: <span id="output"></span>
People who had answered here came with a brilliant idea of wrapping the text into a <div> having zero margin, border and padding;
I just developed the idea further. I place the div inside the container, making the text have exactly the same style as it had without wrapper.
JsFiddle
This solution will work almost everywhere. It can be broken by not very encouraged way of writing CSS, like
.container div b {
padding: 5px; /* firing only when test is run */
}
If you do not code CSS in you project like that, you are the lucky one to use my snippet )

Width of overflowing span inside div, IE9

I need to find out if the contents of a span is overflowing its parent div. It works fine in Chrome and FF, but not in IE9. I have the following HTML structure:
<div class="wrapper">
<span>Dynamic text content, which may or may not overflow the parent</span>
</div>
With the following CSS:
.wrapper {
display: inline-block;
width: 80px;
height: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
In "real" browsers (i.e. not IE), it is easy to check if the span is wider than the div:
var innerSpan = $('.wrapper span');
var wrapperDiv = innerSpan.parent();
if (innerSpan.width() > wrapperDiv.width()) {
// Overflow has happened
}
But in IE9, the call to innerSpan.width() only returns the visible size, which is of course always smaller that the wrapper's size. How can I detect if the text has overflown in IE9?
NOTE: It only needs to work for IE9, not IE8, IE7 or any other version.
EDIT
I found a solution, which detects overflow but requires the span to have display: block;. See my answer below.
The height of your span is 18px as the height of text.When the text overflows the .wrapper div automatically the heigth of span increases.
var height=$('.wrapper span').css("height").replace('px','');
console.log(height);
if(parseFloat(height) > 18){
console.log("overflow occured");
}
DEMO
The element must be present in the DOM in order for the width to be calculated. If you need to calculate this before the user can see the element, try hiding it before putting it into the DOM.
Code Modified:
var innerSpan = $('.wrapper span');
var wrapperDiv = innerSpan.parent();
innerSpan.hide();
alert(innerSpan.width());
alert(wrapperDiv.width());
if (innerSpan.width() > wrapperDiv.width()) {
// Overflow has happened
}
innerSpan.show();
Demo : http://jsfiddle.net/dWzeQ/2/
Hope this will help you!
It worked when I forced the span itself to be block-level, and set the overflow and text-overflow attributes on it. Then I could use scrollWidth and offsetWidth. Here's the code:
HTML:
<div class="wrapper">
<span class="inner">Dynamic text content, which may or may not overflow the parent</span>
</div>
CSS:
.wrapper {
display: inline-block;
width: 80px;
height: 20px;
}
.inner {
display: block;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
And finally, the Javascript:
var innerSpan = $('.inner');
if (innerSpan[0].scrollWidth > innerSpan[0].offsetWidth) {
console.log("Overflow!");
}

Div and textarea behave the same except in Firefox - what to do?

I want to create a textarea which highlights the text beyond a character limit (like the twitter one).
My attempt is here: http://jsfiddle.net/X7d8H/1/
HTML
<div class="wrapper">
<div class="highlighter" id="overflowText"></div>
<textarea id="textarea1" maxlength="200"></textarea>
</div>
<div id="counter">Letters remaining: 140</div>
<input type="Button" value="Done" id="doneButton"></input>
CSS
* {
font-family: sans-serif;
font-size: 10pt;
font-weight: normal;
}
.wrapper {
position: relative;
width: 400px;
height: 100px;
}
.wrapper > * {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
padding: 0;
margin: 0;
border: 0;
overflow: hidden;
resize: none;
white-space: pre-wrap; /* CSS3 */
white-space: -moz-pre-wrap; /* Firefox */
white-space: -pre-wrap; /* Opera below 7 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* IE */
}
.highlighter {
background-color: #eee;
color: #f0f;
}
.highlight {
background-color: #fd8;
color: #f0f;
}
textarea {
background-color: transparent;
color:#000;
}
JAVASCRIPT
function limitTextSize(e) {
var max = 140
var txt = $("#textarea1").val();
var left = txt.substring(0, max);
var right = txt.substring(max);
var html = left + '<span class="highlight">' + right + "</span>";
$("#overflowText").html(html);
$("#counter").html("Letters remaining: " + (max - txt.length));
$("#doneButton").attr("disabled", txt.length > max);
}
function maxLength(el) {
if (!('maxLength' in el)) {
var max = el.attributes.maxLength.value;
el.onkeypress = function () {
if (this.value.length >= max) return false;
};
}
}
$(document).ready(function() {
$("#textarea1").bind('input propertychange', limitTextSize)
maxLength($("#textarea1"));
});
It uses JQuery
It works except on firefox. To see the bug, paste this into the textarea:
fjdf hkj hfj hdfkjsd hfllll sdfl sdlflldsf lsdlf flsdlf lsdf lsdf llsdfls dlfs ldflsd f
Which exposes the small difference in formatting between div and textarea (in firefox only). I've made the 'hidden' text purple so you can see the word wrap difference.
I've looked here: How to force Firefox to render textarea padding the same as in a div?
And here: Wrapping the text the same way in a div as in a textarea
And here: Firefox textarea sizing bug?
But none of those seem to apply...
I thought about trying to make it a contenteditable div but getting the change events looks like a minefield.
Has anyone here done this successfully?
I think you are running into an issue where Firefox adds 1.5px of padding inside textarea elements.
Firefox has had quite some issues with paddings in combination with textareas in the past, I think you might not be able to get rid of these additional 1.5px of padding.
I was able to fix your wrapping issue by setting some vendor specific prefixed CSS properties on div.highlighter. Here's a jsFiddle.
.highlighter {
background-color: #eee;
color: #f0f;
-moz-box-sizing: border-box;
-moz-padding-end: 1.5px;
-moz-padding-start: 1.5px;
}
Setting these properties ensures that
In Firefox, the padding set on the div does not increase the width of the div, and
that, in Firefox, 1.5px of padding will be set on both the right and the left hand side of the div.
Update
After some time of using 2px and still very occasionally experiencing some wrapping inconsistencies, I decided to give 1.5px a go, and for now that seems to have ironed out the occasional inconsistencies.
This has to do with the font size being used. Since the unit used is point (pt), the size calculated is different enough in the browsers to cause the incorrect line wrap.
Try these styles instead:
* {
font-family: sans-serif;
font-size: 12px;
font-weight: normal;
}
body {
font-size: 1em;
}
JSFiddle
You might have to make changes in the container sizes to accomodate the change in font-size.
Okay, couple of things going on here. Generally, the safest cross-browser displayed element you'll find is the pre tag. It assumes that what you're feeding it is "pre-formatted," hence the name. This will benefit us in a couple ways:
As far as I know, there is no default styling done by any major browser done on the pre element.
The pre element will retain leading/trailing whitespace, tabs and other special characters in a box.
Replace the span.highlighter with pre.highlighter
That'll get us started. The second thing we'll want to look at is the overlaid colors creating some rather bizarre stacking effects in Firefox. The text looks out of focus in FF20, and I can only imagine that letting a browser decide how that looks would be a catastrophe going forward.
Set the color of the textarea to transparent.
Now we're there. I'm seeing consistent wrapping in IE10/9, FF20, and Chrome 26.
Here's an example jsFiddle

Add dots/ellipsis on div/span element overflow without using jquery

Need to implement functionality similar to what dotdotdot jQuery plugin does
but cannot use javascript frameworks (like jquery or ext).
Is there any easy way to add the dots to the content of div or span element if content takes more space then element should???
(similar to what css overflow: ellipsis setting does)
Can't use ellipsis beacause it doesn't work with many lines when height is limited.
Thank you :)
Why not using the CSS property text-overflow? It works great as long as you define a width in your tag.
Class in CSS:
.clipped {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
<div class="clipped" style="width: 100px;" title="This is a long text">This is a long text<div>
You can also add the text to the title attribute, so the user can see the whole text when hovering over the element.
Works for any number of lines and any width without any javascript - and is responsive. Simply set your max-height to a multiple of your line height: i.e. (22px line height) * (max 3 lines of text) = (max height 66px).
https://codepen.io/freer4/pen/prKLPy
html, body, p { margin: 0; padding: 0; font-family: sans-serif;line-height:22px;}
.ellipsis{
overflow:hidden;
margin-bottom:1em;
position:relative;
}
.ellipsis:before {
content: "\02026";
position: absolute;
bottom: 0;
right:0;
width: 1.8em;
height:22px;
margin-left: -1.8em;
padding-right: 5px;
text-align: right;
background-size: 100% 100%;
background: linear-gradient(to right, rgba(255, 255, 255, 0), white 40%, white);
z-index:2;
}
.ellipsis::after{
content:"";
position:relative;
display:block;
float:right;
background:#FFF;
width:3em;
height:22px;
margin-top:-22px;
z-index:3;
}
/*For testing*/
.ellipsis{
max-width:500px;
text-align:justify;
}
.ellipsis-3{
max-height:66px;
}
.ellipsis-5{
max-height:110px;
}
<div class="ellipsis ellipsis-3">
<p>Here we can have a great many lines of text and it works as we expect it to. Here we can have a great many lines of text and it works as we expect it to. Here we can have a great many lines of text and it works as we expect it to. Here we can have a great many lines of text and it works as we expect it to.</p>
</div>
<div class="ellipsis ellipsis-5">
<p>The number of lines shown is easily controlled by setting the max-height of the .ellipsis element. The downsides are the requirement of a wrapping element, and that if the text is precisely as long as your number of lines, you'll get a white area covering the very trailing end of your text. You've been warned. This is just some pushing text to make the element longer. See the ellipsis? Yay.</p>
</div>
You could try:
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
This will only work if your elements are not dynamically sized. They will have to have a width set or some other mechanism to keep them from growing to allow more content.
My solution to my problem can seem a little awkward, but it works for me:)
I used a little of CSS:
word-wrap: break-word;
and Javascript:
var spans = document.getElementsByTagName("span");
for (var i in spans) {
var span = spans[i];
if (/*some condition to filter spans*/) { // just
if (navigator.appName == 'Microsoft Internet Explorer') {
span.parentNode.style.display ='inline-block';
}
if (span.parentNode.clientHeight > 50 ) {
span.innerHTML = span.innerHTML.substr(0, 26) + ' ...';
}
}
}
FOR ALL Browser:
.dotdot{ white-space: nowrap; text-overflow: ellipsis; overflow: hidden; max-width:80px}
.dotdot:before { content: '';}
<div class="dotdot">[Button Text Goes here][1]</div>

Categories

Resources