scrollHeight not resetting after programmatically changing content - javascript

I am trying to learn a few things without jQuery. Here is one of the challenges I'm facing.
I have a fixed contenteditable div that when adding text to the div, if the scrollHeight exceeds the clientHeight I shrink the font until content fits the div.
Occasionally I "rebuild" the text which replaces the innerHTML programmatically. Or the user can delete text which should reduce the scrollHeight, but in both cases, the scrollHeight remains the maximum value. I need some way to increase the font size to "fit" the div again. (that ideally isn't super expensive)
Example:
My clientHeight = 142, and the scrollHeight = 158. A loop reduces the font size, until scrollHeight is 142.
Then, the user deletes a line of text, but the scrollHeight is still 142, no change.
code to reduce/increase height:
var textBox = document.getElementById('text');
var current, min = 6, max = 14;
current = textBox.style.fontSize.substr(0, textBox.style.fontSize.length - 2);
current = parseInt(current);
if (textBox.clientHeight < textBox.scrollHeight) {
while (textBox.clientHeight < textBox.scrollHeight) {
current--;
if (current < min) break;
textBox.style.fontSize = '' + current + 'pt';
}
} else if (textBox.clientHeight > textBox.scrollHeight) {
while (textBox.clientHeight > textBox.scrollHeight) {
current++;
if (current > max) break;
textBox.style.fontSize = '' + current + 'pt';
}
}
html (incase it matters):
<div id="text" contenteditable="true"></div>
css (incase it matters):
#text {
position: relative;
border: 1px solid blue;
top: 180px;
left: 31px;
width: 300px;
height: 132px;
padding: 5px;
font-family: 'mplantin';
font-size: 14pt;
font-weight: 200;
}

I was on the same boat, but with an iframe; I'm not sure if my solution suits your chat window because its for page transitioning, but after some testing this is my hack. "content" is the id of an iframe and this is executed inside a javascript function that is called when the page change is needed:
var c=document.getElementById("content");
c.width=0;
c.height=0;
c.src="page.html";
the `src' assignment method expands the values set to 0 right after, achieving the desired result; there may be a way for you to constantly re-size a text area like that; however, I had visual issues with you; I ended up using timers so that the change would take place while the transition between pages was transparent.

This seemed to fix my issue:
element.style.height = "auto";

both answers from #nixahn and #jeff are working for me (chrome,ff)
iframe.style.height ="0"; // or "auto"
iframe.contentWindow.document.open();
iframe.contentWindow.document.write('<style>'+css+'</style>');
iframe.contentWindow.document.write(html);
iframe.contentWindow.document.close();

I have used a div with a fixed height, and the problem with auto is that it resizes the element, I fixed that with the following code after my inner HTML was set:
element.style.height = "auto";
element.style.height = "400px";
now scrollHeight is resetted correctly and gives the real height of the inner HTML

I had this same issue -- A content editable div whose scrollHeight wouldn't shrink when lines were removed.
The accepted answer didn't fix the problem for me, however, removing the div's parent's display: flex; did.

Related

Reduce font-size when long word overflows horizontally

I have long titles that sometimes slightly overflow the window on small screens, so I made a script that reduces the font-size of the title until it fits its container only when the title overflows its container… It works but I'm sure a lighter solution could be found.
function adaptFontSize(titles) {
$(titles).each(function() {
var title = $(this),
reducedFontSize,
counter = 0;
if (title[0].scrollWidth <= title.innerWidth()) { // if title <= container
title.css('font-size', ''); // remove previously added font-size
}
while (title[0].scrollWidth > title.innerWidth() && counter < 20) { // while title > container
var fontSize = reducedFontSize || parseInt(title.css('font-size')); // current title font-size
reducedFontSize = Math.floor((fontSize - fontSize / 20) * 100) / 100; // reduce the font-size
title.css('font-size', reducedFontSize + 'px'); // apply the reduced font-size
counter++ // counter to limit the loop in case title[0].scrollWidth is not supported
}
});
}
or here's a JSFiddle: https://jsfiddle.net/Raoulito/pek7b3mr/12/
My problem comes when I try to apply the script on $(window).resize(). It actually works but I can feel that the process is heavy. It has to work in both ways:
reduce the font-size if the title's too wide,
increase the font-size when the window gets resized wider, until the title reaches its original font-size.
Here are some of steps I tried but did not manage to achieve:
that it does not fire the function all the way through at each pixel the window is resized, but rather wait until it stops resizing.
when the window gets resized wider, only target—among the titles which are smaller than the window—the ones that have already been resized and might need to get bigger again.
So I'm looking for a way to fix these, or alternatively for a way of reaching this in a much more efficient way.
You can use the VW unit.
1vw equals 1% of the view width, so it's a simple css only solution which works in all modern browsers.
https://jsfiddle.net/pek7b3mr/8/
body {
margin: 1em;
font-family: sans-serif;
}
div {
background-color: yellow;
font-size: 5vw;
margin-bottom: 1rem;
}
<div>ABCDEFGHIJKLMNOPQRSTUVWXYZ 012356789</div>

iframe height for values lower than 150px

I want to change the iframe height based on how the iframe content changes, but I'm having problems if the iframe's content has a height value lower than 150px.
I've tested this code in Firefox and Chrome and it has the same result.
Here you can test it https://jsfiddle.net/bqvc0pp5/17/
From what I've seen if no height is specified the iframe will automatically have 150px(without border).
In this example I increased the value to 300px then lowered to 200px than to 50px . Everything works great until the 50px is converted to 150px;
var iframe = document.createElement('iframe');
iframe.setAttribute("scrolling","no");
iframe.onload = () => {
builtElement();
};
//append iframe to main document
document.body.appendChild(iframe);
var k=0;
function builtElement(){
var div = document.createElement('div');
div.style.height="50px";
div.style.backgroundColor="salmon";
iframe.contentWindow.document.body.style.margin="0px";
iframe.contentWindow.document.body.appendChild(div);
div.addEventListener("click", () =>{
if(k==0){
div.style.height="300px";
}
if(k==1){
div.style.height="200px";
}
if(k==2){
div.style.height="50px";
}
if(k>=2) k=0;
else k++;
updateIframeHeight();
});
}
function updateIframeHeight(){
//I need to reset height or else scrollHeight will be the last max value
iframe.style.height="";
var height = iframe.contentWindow.document.body.scrollHeight;
//this is a dirty hack where I can check if offsetHeight is lower than 150px
var offsetHeight = iframe.contentWindow.document.body.offsetHeight;
//if(offsetHeight<150) height=offsetHeight;
console.log("scrollHeight: "+height);
console.log("scrollHeight: "+offsetHeight);
iframe.style.height=height+"px";
}
If I do a little hack and check if the offsetHeight is lower than 150px it works.
var offsetHeight = iframe.contentWindow.document.body.offsetHeight;
if(offsetHeight<150) height=offsetHeight;
Is this the best way to do this?
UPDATE: In a real example, the iframe body contains more than one elements, but only certain elements change height, therefor changing the body scrollHeight
I found the solution. All you have to do is set the size to 0px;
iframe.style.height="0px";
What I did previously iframe.style.height=""; set the height to 150px by default. It appears that if no size is specified the iframe size will be 150px;
this is not the final solution but this may help you to fix your issue without hacks:
[Fiddle][1]https://jsfiddle.net/Marouen/qskpdn8b/

How can I keep my scroll bar size(length) as stable?

I have a scenario here, there is a div parent element. it has the css value as overflow:auto. when the user keep add multiple element, the overflow works, but the size of the scroll bar going small. (that's common!)
What i looking is, i don't want to make my scroll bars height became small or big. i would like to keep my scrollbar height became static, whatever the length of elements appended.
So I trying to hide and show the nested elements under the parent. instead of resizing the scroll bar i re-sizing the content.
I don't have much idea about this. but nearly i tried to get the one what i look.
but not get the result.
here is my code :
var $newdiv = $('.div');
$col = $('<div />');
var container = $('#content');
var n = 0;
var child = $('.show');
var parent = container;
var add = function () {
for(var i=1; i<=10; i++){
$newdiv.clone().removeClass('div').addClass('show')
.find('span').append(n = n < 9 ? '0'+(++n) : ++n).end().clone().appendTo($col);
}
}
$col.appendTo(container);
$('button').click(function () { add()});
$(container).slimScroll({});
container.scroll(function(){
totalCH = 20*n;
parentH = parent.outerHeight();
required = Math.abs(parentH - totalCH);
scrolled = $(this).scrollTop();
requireToHide = scrolled/20;
hidableNo = Math.round(requireToHide);
if(scrolled > lastScrollTop) {
//while scroll down
//$(".div:lt("+hidableNo+ ")" ).addClass('hideIt');
//$(".div:gt("+(n-1)+ ")" ).addClass( "showIt");
} else {
//while scroll up
//$(".div:lt("+(n-1)+ ")" ).addClass("showIt");
//$(".div:gt("+hidableNo+ ")" ).addClass("hideIt");
}
lastScrollTop = scrolled;
});
var lastScrollTop = 0;
Try Online
Any one suggest me the correct approach.
Thanks in advance!
Please avoid plug-in and consider the support ie9
I'm going to refer to "scroller" as the draggable element in the scrollbar.
The scroller changing size is a standard UI representation that lets the user understand the amount of content in the scroll area; the goal of which is also to keep the rate of scrolling of the content to a standard flow rate.
For example, if you have a large amount of content in the scrollable area, dragging on a smaller scroller means that the content will flow at (about) the same speed as having less scrollable content (and a larger scroller).
Having a constant size scroller, will mean that a large amount of content would need to scroll much faster than a smaller amount of content.
To achieve what you want, I believe you're going to need an addon that takes the scroll speed/acceleration into account.
I use: jquery.mCustomscroll bar for custom scrolling behaviors.
http://manos.malihu.gr/jquery-custom-content-scroller/
It even has a setting to do exactly what you want:
autoDraggerLength: boolean
Enable or disable auto-adjusting scrollbar dragger length in relation
to scrolling amount (same behavior with browser’s native scrollbar).
Set autoDraggerLength: false when you want your scrollbar to (always)
have a fixed size.
You can solved with css
::-webkit-scrollbar{
width: 10px;
}
::-webkit-scrollbar-track{
-webkit-box-shadow: inset 0 0 1px rgba(0,0,0,0.3);
border: 1px solid black;
background: rgb(41,41,41);
border-radius: 10px;}
::-webkit-scrollbar-thumb{
border-radius:10px;
width: 8px;
height: 150px;
border: 1px solid black;
background: white;
}

JavaScript: Get window width minus scrollbar width

Ok, I thought this would be really simple, but it's turning out not to be. I think I'm just messing something up in my HTML/CSS, but here goes.
I have a basic page like so:
HTML
<!DOCTYPE html>
<html>
<head>
<link href='test2.css' rel="stylesheet" type="text/css" />
<script src="http://code.jquery.com/jquery-1.10.2.js"></script>
<script src="test2.js"></script>
</head>
<body>
<div id="scroll"></div>
</body>
</html>
test2.css
* {
padding: 0;
margin: 0;
}
html, body {
height: 100%;
width: 100%;
}
#scroll {
height: 100%;
width: 100%;
overflow: scroll;
background-color: black;
}
test2.js
$(document).ready(function() {
// my resolution is 1440x900
alert('innerwidth should be 1425');
// all of these return 1440
alert('body innerwidth: ' + $('body').innerWidth());
alert('document width: ' + $(document).width());
alert('window width: ' + $(window).width());
alert('scroll div innerwidth: ' + $('#scroll').innerWidth());
alert('document.documentElement.clientWidth: ' + document.documentElement.clientWidth);
alert('document.documentElement.scrollWidth: ' + document.documentElement.scrollWidth);
});
So I've got one element on the page... a div that takes up the entire screen, or rather it should be taking up the entire screen minus the scrollbars. Now, I've been doing some snooping on how to grab the width and height of a page without the scrollbars, but unfortunately, none of them return the proper value... which makes me believe I'm missing the boat in my HTML or CSS.
I looked at the following:
jquery - how to get screen width without scrollbar?
how to get the browser window size without the scroll bars
So what I need is for a method to return the value of my viewable screen minus the respective scrollbar value... so for my width, my value should be 1425 because the scrollbar is 15 pixels wide. I thought that's what innerWidth's job was, but apparently I'm wrong?
Can anyone provide any insight? (I'm running Firefox 24.)
EDIT
To add some background, I've got a blank page. I will be adding elements one by one to this page, and I need to use the width of the page when calculating the sizes for these elements. Eventually, this page will grow and grow until the scrollbar appears, which is why I'm trying to force the scrollbar there from the start, but apparently, that still doesn't do anything.
EDIT2
Here's something even more interesting... if I do document.getElementById('scroll').clientWidth, I get the proper innerWidth, but if I do $('#scroll').width() or $('#scroll').innerWidth(), they both return the max resolution... sounds like a jQuery bug.
I got this somewhere and would give credit if I knew where, but this has been succesfull for me. I added the result as padding when setting the html overflow to hidden.
Problem is that the scrollbar is a feature of the browser and not the web page self. Measurement should be done dynamically. A measurement with a scrollbar and a measurement without a scrollbar will resolve into calculating the difference in width.
Found the source: http://www.fleegix.org/articles/2006/05/30/getting-the-scrollbar-width-in-pixels
scrollCompensate = function () {
var inner = document.createElement('p');
inner.style.width = "100%";
inner.style.height = "200px";
var outer = document.createElement('div');
outer.style.position = "absolute";
outer.style.top = "0px";
outer.style.left = "0px";
outer.style.visibility = "hidden";
outer.style.width = "200px";
outer.style.height = "150px";
outer.style.overflow = "hidden";
outer.appendChild(inner);
document.body.appendChild(outer);
var w1 = inner.offsetWidth;
outer.style.overflow = 'scroll';
var w2 = inner.offsetWidth;
if (w1 == w2) w2 = outer.clientWidth;
document.body.removeChild(outer);
return (w1 - w2);
}
var htmlpadding = scrollCompensate();
The correct answer is in this post marked as accepted:
CSS media queries and JavaScript window width do not match
This is the correct code:
function viewport() {
var e = window, a = 'inner';
if (!('innerWidth' in window )) {
a = 'client';
e = document.documentElement || document.body;
}
return { width : e[ a+'Width' ] , height : e[ a+'Height' ] };
}
Discovered a very hacky solution... by adding this before my alerts in test2.js, I get the proper width:
var p = $('body').append('<p style="height: 100%; width: 100%;"></p>');
alert(p.width());
$('body').remove('p');
And consequently, all of the alerts now have the proper width. I also don't even need overflow-y in the CSS if I do it this way. Curious why this solves it...
The real answer should be keeping the HTML and CSS as is, then using document.getElementById('scroll').clientWidth. Using clientWidth gets the viewable area minus the scrollbar width.
The correct width of the page is given by $(document).width().
Your problem is that you're using a scroll within the div (overflow: scroll).
Using $(document).width() the returned value is already discounting the visible width of the scroll, but how do you put a scroll within the div value returned is no longer the same.
As the width of the scroll is not standard and varies from system to system and browser to browser, it is difficult to solve.
I suggest you remove the scroll of the div and let the browser manage this by default in the body, then yes you have the correct width.

Full height content with the smallest possible width

I would never think this is possible, but there are a lot of clever people here so I thought I'd ask. I'm looking for a way to have a full-height container whose width depends on how much content there is. I want the text to fill the area taking up the full height while using the smallest possible width. The height is known and hard-coded, the amount of content is not.
I'm working with something like this:
<div>
<p>Lorem Ipsum is simply dummy text of the printing and typesetting industry.
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
when an unknown printer took a galley of type and scrambled....</p>
</div>
div {
background:red url(http://lorempixel.com/600/400/);
float:left;
width:600px;
height:400px;
}
p {
background:#fff;
padding:20px;
margin:20px;
}
Normally content fills the page from top to bottom:
What I'm looking for is sort of the opposite, filling in left-to-right:
With less content, it should look like this:
Using full hard-coded height with width:auto produces this effect:
Is there any way to have the text fill the height with the smallest possible width, without hard-coding a width or having text overflow? It seems impossible and I have no idea how to approach it. Javascript/jQuery solutions welcome.
What I have in mind is a simple jQuery solution: in a while loop, set the condition such that the loop is run whenever the height exceeds the container height. In the loop, you increase the width of <p> pixel by pixel until the height no longer exceeds container height :)
$(document).ready(function() {
// The 400-80 is because you have to subtract the container padding and the element's own padding
while($("div > p").height() > 400 - 80) {
currentWidth = $("div > p").width();
$("div > p").width(currentWidth + 1);
}
});
http://jsfiddle.net/teddyrised/RwczR/4/
I have made some changes to your CSS, too:
div {
background:red url(http://lorempixel.com/600/400/);
float:left;
overflow: hidden;
width:600px;
height:400px;
padding: 20px;
box-sizing: border-box;
}
p {
background:#fff;
padding: 20px;
width: 1px;
}
This is not too difficult to do with JavaScript. I have no idea how to do it without JS (if that's even possible).
You can use another "invisible" div to measure dimensions until it gets to the 320px height while reducing its with by a set amount (even 1 pixel at a time, if you want to be as precise as possible).
var $measurer = $("<div>").css({'position': 'fixed', 'top': '100%'})
.text($("p").text()).appendTo('body');
var escape = 0;
while ($measurer.height() < 320) {
console.log($measurer.height());
$measurer.width(function (_, width) { return width - 1; });
console.log($measurer.width());
escape++;
if (escape > 2000) {
break;
}
}
$("p").width($measurer.width());
$measurer.remove();
http://jsfiddle.net/RwczR/2/
Try this:
var p = $('p');
var height = parseInt(p.height())-40;
p.height('auto');
p.width('auto');
for(var i=p.width(); i--; ) {
p.width(i);
if (p.height() > height) {
p.height(height+20);
p.width(i-1);
break;
}
}
p.height(height);
http://jsfiddle.net/RwczR/6/
You can use jQuery/JavaScript and checking the client vs the scroll heights keep increasing the width until it fits the text, similar to the below.
You need to also set overflow: hidden; in the CSS on the p tag for the scrollHeight to give you the actual height including the overflow.
The below code also takes margin and padding into account for both; height and width and adjusts accordingly.
Changing the height of the outer div ajdust accordingly.
$(document).ready(function(){
var $container = $("div");
var containerHeight = $container.height();
var containerWidth = $container.width();
var $textWrapper = $(">p", $container);
var paddingMarginHeight = $textWrapper.outerHeight(true) - $textWrapper.innerHeight();
var paddingMarginWidth = $textWrapper.outerWidth(true) - $textWrapper.innerWidth();
$textWrapper.innerHeight(containerHeight - paddingMarginHeight);
//SetMinWidth();
var maxWidth = containerWidth - paddingMarginWidth;
var visibleHeight = 0;
var actualHeight = 0;
for(i = 50; i <= maxWidth; i++){
$textWrapper.innerWidth(i);
visibleHeight = $textWrapper[0].clientHeight;
actualHeight = $textWrapper[0].scrollHeight;
if(visibleHeight >= actualHeight){
break;
console.log("ouyt");
}
}
});
DEMO - Grow width until text is fully visible
We can give the paragraph an overflow:auto;.
If the paragraph needs a vertical scroll bar, it will create one.
The trick is to keep tightening the width, until the scroll bar is created.
var hasScrollBar = false;
var p = document.getElementById('myParagraph');
while(!hasScrollBar)
{
if(p.scrollHeight>p.clientHeight)
{
//Has Scroll Bar
//Re-Increase Width by 1 Pixel
hasScrollBar=true;
p.style.width=(p.clientWidth+1)+"px";
}
else
{
//Still no Scroll Bar
//Decrease Width
p.style.width=(p.clientWidth-1)+"px";
}
}

Categories

Resources