How to calculate the width of the scroll bar? - javascript

Given a <textarea> with a fixed width, I would like its "active width" to be constant (in px). By "active width" I mean the area where the text appears.
When the vertical scroll bar doesn't appear, the "active width" equals to width. But, when the vertical scroll bar appears, the "active width" becomes smaller than width (I guess smaller exactly by the width of the scroll bar).
I thought to identify whether the vertical scroll bar appears or not, and if yes, to increase the width of the <textarea> by the width of the scroll bar. How could I identify the width of the scroll bar?
Is there a better approach?
(I'm interested in Firefox, if it makes the life easier.)

There is a jQuery plugin that can help with this: https://github.com/brandonaaron/jquery-getscrollbarwidth/blob/master/jquery.getscrollbarwidth.js
Also, from http://www.alexandre-gomes.com/?p=115
Here is some code that may help.
This creates a hidden <p> element at 100% width inside a <div> with a scrollbar, then calculates the <div> width - the <p> width = scroll bar width.
function getScrollBarWidth () {
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);
};

Scrollbar width is simply (offsetWidth - clientWidth) in a borderless! element.
This function calculates it on the fly and caches the result for further use. No need need for percentage width etc.
var getScrollbarWidth = function() {
var div, width = getScrollbarWidth.width;
if (width === undefined) {
div = document.createElement('div');
div.innerHTML = '<div style="width:50px;height:50px;position:absolute;left:-50px;top:-50px;overflow:auto;"><div style="width:1px;height:100px;"></div></div>';
div = div.firstChild;
document.body.appendChild(div);
width = getScrollbarWidth.width = div.offsetWidth - div.clientWidth;
document.body.removeChild(div);
}
return width;
};

I believe this is a more straightforward solution: (assuming body {width:100%;})
function calculateScrollBarWidth() {
return window.innerWidth - document.body.clientWidth;
}
A solution to calculate the scrollbar width of any element (e.g. a div with overflow, or a textarea)
function calculateScrollbarWidth(element) {
if (!element) {
// Return the body scrollbar width, when no element was specified.
return window.innerWidth - document.body.clientWidth;
} else {
// When an element is specified, return its specific scrollbar width.
return element.offsetWidth - element.clientWidth;
}
}

Maybe you could put
overflow-y:scroll;
in your css. This forces the scrollbar to be present even when the text area is blank, so the width is always constant.

Using jQuery, simply write:
function getScrollBarWidth () {
var $outer = $('<div>').css({visibility: 'hidden', width: 100, overflow: 'scroll'}).appendTo('body'),
widthWithScroll = $('<div>').css({width: '100%'}).appendTo($outer).outerWidth();
$outer.remove();
return 100 - widthWithScroll;
};

I was looking for something similar - here is what i found
function measureScrollbar() {
var $c = $("<div style='position:absolute; top:-10000px; left:-10000px; width:100px; height:100px; overflow:scroll;'></div>").appendTo("body");
var dim = {
width: $c.width() - $c[0].clientWidth,
height: $c.height() - $c[0].clientHeight
};
$c.remove();
return dim;
}
Source : Slickgrid uses this - https://github.com/mleibman/SlickGrid/blob/master/slick.grid.js#L378
Or
Use Google Closure Library -link
/**
* Returns the scroll bar width (represents the width of both horizontal
* and vertical scroll).
*
* #param {string=} opt_className An optional class name (or names) to apply
* to the invisible div created to measure the scrollbar. This is necessary
* if some scrollbars are styled differently than others.
* #return {number} The scroll bar width in px.
*/
goog.style.getScrollbarWidth = function(opt_className) {
// Add two hidden divs. The child div is larger than the parent and
// forces scrollbars to appear on it.
// Using overflow:scroll does not work consistently with scrollbars that
// are styled with ::-webkit-scrollbar.
var outerDiv = goog.dom.createElement('div');
if (opt_className) {
outerDiv.className = opt_className;
}
outerDiv.style.cssText = 'overflow:auto;' +
'position:absolute;top:0;width:100px;height:100px';
var innerDiv = goog.dom.createElement('div');
goog.style.setSize(innerDiv, '200px', '200px');
outerDiv.appendChild(innerDiv);
goog.dom.appendChild(goog.dom.getDocument().body, outerDiv);
var width = outerDiv.offsetWidth - outerDiv.clientWidth;
goog.dom.removeNode(outerDiv);
return width;
};

I think this helps to get the width of scrollbar.
textarea.scrollHeight
gives the height so cant this be used..

CSS:
.scrollbar-measure {
width: 100px;
height: 100px;
overflow: scroll;
position: absolute;
top: -9999px;
}
Vanilla JS:
// Create the measurement node
var scrollDiv = document.createElement("div");
scrollDiv.className = "scrollbar-measure";
document.body.appendChild(scrollDiv);
// Get the scrollbar width
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
console.warn(scrollbarWidth); // Mac: 15
// Delete the DIV
document.body.removeChild(scrollDiv);
Solution by David Walsh

With jQuery:
var t = jQuery('<textarea/>').css({
position: 'absolute',
top: '-100px',
overflowX: 'hidden',
overflowY: 'scroll'
}).prependTo('body'),
w = t[0].offsetWidth - t[0].clientWidth;
console.log('bar width = ', w);
t.remove();
bar width = 18

Related

Change an element width in px when resizing the window

How can I change an element with a fixed width in px when i resize the window? I need to use an absolute unit, i can not use relative units like % or vw.Every time the window is resized with 1 px i need to decrease the element width by 0.2px.
I tried to use the window resize eventListener but i don't know what calculations needs to be done.
What you want can be achieved by using javascript. I've created a logic to do that :
<script>
function myFunction() {
var initialscreenwidth = window.innerWidth; //Set Initial Screen Width
setInterval(function() { //looping the script
var screenwidth = window.innerWidth;
var difference = initialscreenwidth - screenwidth; //Calculating the change in screen-size
if (difference != 0) { //Checking if there is a change in width
var element = document.getElementById('demo');
var style = window.getComputedStyle(element, null).getPropertyValue('font-size'); //Getting default font-size of the element
var initialfontsize = parseFloat(style);
var fontdifference = -(parseFloat(difference) / 5); //1px change in screen-size = 0.2px change in font-size
var newfontsize = initialfontsize + fontdifference;
var newfontsizepx = newfontsize + "px";
if (newfontsize > 1) {
document.getElementById("demo").style.fontSize = newfontsizepx;
}
}
initialscreenwidth = window.innerWidth;
}, 300); //reloads in every 300ms
}
myFunction();
</script>
Paste this at the end of your body section, somehow using this in the head section is not working.

How to expand div according to position in viewport

I am trying to get a div to expand div to always fill the viewport;
at the moment i calculate the if the div is left ,rigth above or below the center of the viewport and expand accordingly.
I would prefer if the div would expand to center in the viewport if possible. atm the moment i am just adjusting the margin to allow the div to expand sideways. but if the expanded div is big it can expand passed the viewport on a side while having enough space on the otherside.
this has to be in vannilla javascript. My current code follows:
function calculateExpandDir(el) {
var ele = el.getBoundingClientRect();
var windowH = window.innerHeight;
var windowW = window.innerWidth;
var expandDir = {}
if (ele.left + (ele.width / 2) >= window.innerWidth / 2) { expandDir.horizontal = "left" } else { expandDir.horizontal = "right" }
if (ele.top + (ele.height / 2) >= window.innerHeight / 2) { expandDir.vertical = "up" } else { expandDir.vertical = "down" }
return expandDir
}
function expandEl(el) {
var expandedHeight = parseFloat(el.getAttribute('data-height'));
var expandedWidth = parseFloat(el.getAttribute('data-width'));
var originalWidth = parseFloat(el.getAttribute('data-orig-width'));
var originalHeight = parseFloat(el.getAttribute('data-orig-height'));
var marginTop = 0;
var marginRight = 0;
var marginBottom = 0;
var marginLeft = 0;
var dir = calculateExpandDir(el)
console.log("expand to " + dir.vertical + dir.horizontal)
if (dir.horizontal == "right") {
}
if (dir.horizontal == "left") {
marginLeft = -(expandedWidth - originalWidth);
}
if (dir.vertical == "up") {
marginTop = -(expandedHeight - originalHeight)
}
if (dir.vertical == "down") {
};
el.style.position = "absolute";
el.style.height = expandedHeight+"px";
el.style.width = expandedWidth + "px";
el.style.transition = "all 0.5s ease-in-out";
el.style.marginTop = marginTop+"px";
el.style.marginBottom = marginBottom+"px";
el.style.marginLeft = marginLeft+"px";
el.style.marginRight = marginRight+"px";
}
I figured i have to calculate the ammount of space on each side of the div and expand the div proportionally to the ammount of space available so it will become centered but i am stuck on how to do this at the moment
EDIT:
https://codepen.io/sereal_killer/pen/eGZaNo
in this example the divs expand depending on their position. the center div is a cheat as it has the data attribute to tell it to expand in both directions.
i want the divs to expand to fill the screen depending on the available room (they have a max height and width).
if i have a centered div like in the example i want it to know it has equal room on both sides and expand equally left and right.
if it is a bit more to the right i want it to expand to still be centered.
the caveat is that it cannot leave where it originated. it cant pop out and just appear in the center. If i have time i will draw a picture aswell

How can I inject a div into a webpage that moves on window resize?

Essentially, I'm creating a custom click & drag selection box. The problem is that the div is position absolutely, so it will scroll with the page, but it will not move with the page when the window is being resized. My attempted solution was to listen to the window resize, and move the div according to the change. The problem is that it will SEEM to work, but it will not move entirely accurately, so it will slowly move out of place if the window is resized slowly, or quickly move out of place if the window is resized quickly. It seems that the resize listener does not capture every resize event. I've narrowed the code down to the concept I'm using.
Try injecting this script into a page (I'm using the Chrome console and I haven't made any attempt for cross-compatibility because this will be used in a Chrome extension). It will attempt to resize only when the scrollbar is not active, to replicate the behavior of the page content. The client and scoll variables are interchangeable for recording the change in dimensions, but they are both there for testing purposes. I would love to see a solution which solves this problem using styling attributes. Thanks for your help!
var div = document.createElement("div");
div.style.position = "absolute";
div.style.backgroundColor = "#000";
div.style.width = div.style.height = div.style.left = div.style.top = "200px";
document.body.appendChild(div);
// get the highest z index of the document
function highestZIndex() {
var elems = document.getElementsByTagName("*");
var zIndex = 0;
var elem, value;
for (var i = 0; i < elems.length; i++) {
value = parseInt(document.defaultView.getComputedStyle(elems[i], null).zIndex, 10);
if (value > zIndex) {
zIndex = value;
elem = elems[i];
}
}
return {
elem: elem,
zIndex: zIndex
};
}
// set the div on top if it is not already
var highestZ = highestZIndex();
if (highestZ.elem != div) div.style.zIndex = highestZ.zIndex + 1;
// last width & height of client & scroll to calculate the change in dimensions
var clientWidth = document.body.clientWidth;
var clientHeight = document.body.clientHeight;
var scrollWidth = document.body.scrollWidth;
var scrollHeight = document.body.scrollHeight;
// move the div when the window is being resized
function resizeListener() {
var _clientWidth = document.body.clientWidth;
var _clientHeight = document.body.clientHeight;
var _scrollWidth = document.body.scrollWidth;
var _scrollHeight = document.body.scrollHeight;
// horizontal scrollbar is not enabled
if (_scrollWidth <= _clientWidth) {
div.style.left = parseInt(div.style.left.replace(/px/, ''), 10) + (_scrollWidth - scrollWidth) / 2 + 'px';
}
// vertical scrollbar is not enabled
if (_scrollHeight <= _clientHeight) {
div.style.top = parseInt(div.style.top.replace(/px/, ''), 10) + (_scrollHeight - scrollHeight) / 2 + 'px';
}
clientWidth = _clientWidth;
clientHeight = _clientHeight;
scrollWidth = _scrollWidth;
scrollHeight = _scrollHeight;
}
window.addEventListener("resize", resizeListener);
PS: Please, no jQuery solutions.
Since the resize listener isn't quite dependable with outside events, I've developed a simple "hack" to get the wanted results. The window overflow is forced to scroll and the body width & height are set to +1 so that the scrollbar is active, in which the div will then stay in place. Once the resize is complete, the overflow and body dimensions are restored. This may not be a desired solution for others who want the div to move on a manual window resize, but I am invoking the resize from JavaScript so it works perfectly for me.
The script in practice:
var overflow, overflowX, overflowY, bodyWidth, bodyHeight;
function startResize() {
// store the original overflow values
overflow = document.body.style.overflow;
overflowX = document.body.style.overflowX;
overflowY = document.body.style.overflowY;
bodyWidth = document.body.style.width;
bodyHeight = document.body.style.height;
// force the scrollbar
document.body.style.overflow = "scroll";
// activate the scrollbar
document.body.style.width = document.client.width + 1 + "px";
document.body.style.height = document.client.height + 1 + "px";
}
function stopResize() {
// restore the original overflow values; x & y are included because enabling the global overflow will update x and y
document.body.style.overflow = overflow;
document.body.style.overflowX = overflowX;
document.body.style.overflowY = overflowY;
// restore the original body width & height
document.body.style.width = bodyWidth;
document.body.style.height = bodyHeight;
}

Checking a text element's width (based on font-size) against its parent container

I come to you with a tricky question:
Imagine you have the following basic structure:
<div><p>hello</p></div>
Now assume that div has display:block; and width:200px;.
Using javascript, how would you check what font-size gives you a 'hello' as big as possible without horizontal overflow (in the case of one word) or jumping to a 2nd line in case of a sentence or group of words?
I can't think of a way to measure the space occupied by text so that it can then be checked against that of the parent container, let alone checking if an element is overflowing or linejumping.
If there is a way, I'm sure this is the right place to ask.
Take a look at FitText
It is open source on github as well.
If you are interested in typography you might want to check out their other project called Lettering.js
There may be a method that's not as crazy, but this should be as precise as possible. Essentially, you have a div that you use to measure its width and incrementally increase the text content until it exceeds the width of the target div. Then, change the target div's <p>'s font size to the measuring div's minus 1:
http://jsfiddle.net/ExplosionPIlls/VUfAw/
var $measurer = $("<div>").css({
position: 'fixed',
top: '100%'
}).attr('id', 'measurer');
$measurer.append($("<p>").text($("p").text()));
$measurer.appendTo("body");
while ($measurer.width() <= $("#content").width()) {
$("#measurer p").css('font-size', '+=1px');
console.log($("#measurer").width());
}
$("#measurer p").css('font-size', '-=1px');
$("#content p").css('font-size', $("#measurer p").css('font-size'));
$measurer.remove();
Quick and dirty
fiddle
Set p's style to display: inline then run this
var dWidth = $("div").width();
var pWidth = $("p").width();
var starting = 1;
while (pWidth < dWidth) {
$("p").css("font-size",starting+"em");
pWidth = $("p").width();
starting = starting + .1;
}
Try this:
Auto-size dynamic text to fill fixed size container
(function($) {
$.fn.textfill = function(options) {
var fontSize = options.maxFontPixels;
var ourText = $('span:visible:first', this);
var maxHeight = $(this).height();
var maxWidth = $(this).width();
var textHeight;
var textWidth;
do {
ourText.css('font-size', fontSize);
textHeight = ourText.height();
textWidth = ourText.width();
fontSize = fontSize - 1;
} while ((textHeight > maxHeight || textWidth > maxWidth) && fontSize > 3);
return this;
}
})(jQuery);
$(document).ready(function() {
$('.jtextfill').textfill({ maxFontPixels: 36 });
});
<div class='jtextfill' style='width:100px;height:50px;'>
<span>My Text Here</span>
</div>
new jsFiddle Demo (updated 3/22/13)
I would just keep increasing the font size until the clientWidth or clientHeight changed. However, this becomes unreliable when using the actual element itself. To handle that situation, it is possible to create a span on the fly and then monitor the span's dimensions in order to properly retain the actual element's original sizes.
js
var adjuster = document.getElementById("adjust");
adjuster.onclick = function(){
var p = document.getElementById("p");
var text = p.innerText;
var s = document.createElement("span");
s.innerText = text;
p.innerHTML = "";
p.appendChild(s);
var h = p.clientHeight;
var w = p.clientWidth;
var size = 10;
while(true){
size++;
s.style.fontSize = size + "px";
if($(s).height() > h || $(s).width() > w){
size-=2;//rollback to no height change
s.style.fontSize = size + "px";
break;
}
}
p.style.fontSize = s.style.fontSize;
p.removeChild(s);
p.innerText = text;
};

Detect vertical scroll and scrollbar width and apply width change to body

In my page, I wish to detect whether the page has vertical scrollbars, and if so, need to detect the width of the scrollbar, so I can reduce my body by the width and thus prevent my sidebar from changing location from viewing a non-scrolling page to a scrolling page.
I have the following jQuery/Javascript code:
$(document).ready(function () {
var parent, child, width;
if (width === undefined) {
parent = $('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo('body');
child = parent.children();
width = child.innerWidth() - child.height(99).innerWidth();
parent.remove();
}
if ($("body").height() > $(window).height()) {
//change width of body here
}
});
Unfortunately, this code doesn't work for me. Can someone please let me know where I'm going wrong?
(function($) {
$.fn.ScrollBarWidth = function() {
if (this.get(0).scrollHeight > this.height()) { //check if element has scrollbar
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);
}
}
})(jQuery);
Runs like so :
var scrollbarWidth = $('body').ScrollBarWidth();
console.log(scrollbarWidth);​ //prints the scrollbar width to the console
FIDDLE
You shouldn't need to change the width of the body. By default, it's 100% of the window's width and will adjust when scrollbars appear.
However, if you can't for some reason set the width to 100%, first see if disabling the horizontal scrollbar helps you:
overflow-x: hidden;
If that doesn't cut it, use the function from here to get the scrollbar's width. Then, listen to the window resize event:
var $window = $(window),
$body = $('body');
function resize() {
if ($body.height() > $window.height()) {
$body.width($body.width() - getScrollBarWidth());
}
}
$(window).resize(resize);​

Categories