Can you set the size of an element using getBoundingClientRect - javascript

I have a button that gets resized on the mouse enter event. I am wondering if its possible to resize it based on its height received from getBoundingClientRect().height.
This is what I've tried so far with variations of this but nothing seems to work
btn.onmouseover = function(){
let h = this.getBoundingClientRect().height;
this.style.width = "100%";
if(h != this.getBoundingClientRect().height)
{
this.getBoundingClientRect().height = h;
}
this.style.right = "4%";
}
What happens is the button resizes when the mouse is over the button due to the text inside of it being moved from two lines to one so I was wondering if there is a function call similar to getBoundingClientRect() where i can set the height of the button based on the height I get from h. This way the text inside the button being moved from 2 lines to one doesn't resize the whole button on mouseover.
Also setting the height from h to the style height of the button creates a weird offset so that's not really a viable solution for me in this scenario

I'm confused by this code. You are checking if the height you just obtained from getBoundingClientRect is equal to the height from getBoundingClientRect, and of course it will always be. And no, you can't possibly change the size of something by updating the information in the size you got from calling getBoundingClientRect. Anyway, if you really want to do something in JS relating to hovering, you need to use mouseenter and mouseleave, not mouseover.
But actually, you should be able to do this entirely in CSS.
button { width: 200px; height: 3em; }
button:hover { width: 400px; }
<button>Hi, I'm a button with some pretty long text</button>
If you really want to handle the mouse events yourself, then
const button = document.querySelector('button');
button.addEventListener('mouseenter', () => {
button.style.height = button.offsetHeight + 'px';
button.style.width = '600px';
});
button.addEventListener('mouseleave', () => {
button.style.width = button.style.height = '';
});
button { width: 240px; }
<button>Button with some pretty long text which will wrap</button>

Related

How to get Height of an "element in 'auto 'mode" when it's height actually is not 'auto'? JavaScript

I need to get height of an element when it the height is auto, but I don't want to turn the actual height to 'auto', to find it. Let's say, now the heigh = '100vh'. I don't want to change it to 'auto', but I need to get height in pixels like it would be auto.
Basicly it is a div with a text. I use the following code, but it doesn't work without delay: setTimeout(() => {...}, 1);
<style>
#one {
height:100vh;
width:30vw;
background-color:rgb(1,1,1);
display:block;
float:left;}
</style>
<div id="one">
<p> A lot of text here </p>
</div>
<script>
window.addEventListener('resize', function() {
var one = document.getElementById("one");
one.style.height = 'auto';
if (window.innerHeight > one.clientHeight )
{
one.style.height = '100vh';
}
else
{
one.style.height = 'auto';
}
}, true);
</script>
The problem is that it doesn't have enought time to convert the height to auto and then to excecute the if else statement. So, if I wrap it in setTimeout(() => {...}, 1); delay it starts to work correctly.
But without the delay it doesn't work.
So, if I wrap it it looks like this:
<script>
window.addEventListener('resize', function() {
var one = document.getElementById("one");
one.style.height = 'auto';
setTimeout(() => {
if (window.innerHeight > one.clientHeight )
{
one.style.height = '100vh';
}
else
{
one.style.height = 'auto';
}
}, 0.1);
}, true);
</script>
But in this way while you resize the window it starts to excecute the first condition, and then the second, and then back the first, only because it doen't have time to turn it the height before the if else statement to 'auto'.
So I either need to make it faster to make it workable without the delay(what is not possible for some reason) or to get rid of the temporary changing of the height to auto.
So, basicly I have a little bit more complicated code, but it will take a lot of your time to explore it deeply. So I made it shorter and more readable.
You can just set the height to auto, measure it, and then delete the inline css (or revert it), and as long as you don't have a timeout or anything in between, it will all happen before a repaint can occur anyways - the client will never see it because it never actually happened:
document.getElementById('demo').style.height='auto';
const height = document.getElementById('demo').clientHeight;
document.getElementById('demo').style.height='';
console.log(`${height}px`)
#demo {
height: 100vh;
background: red;
}
<div id="demo">Hi</div>
To prove that it never happens, we have a timeout (just to let initial render complete), then we set the height to auto, busy loop for 10 seconds, then change it back - you'll never see the height change:
setTimeout(() => {
document.getElementById('demo').style.height='auto';
const height = document.getElementById('demo').clientHeight;
const now = Date.now();
while (Date.now() - now < 10000) {}
document.getElementById('demo').style.height='';
console.log(`${height}px`)
}, 100);
#demo {
height: 100vh;
background: red;
}
<div id="demo">Hi</div>

CSS attribute not always being grabbed by javascript correctly

I am trying to resize the side bars whenever the image changes.
I have my javascript trying grab the height of the image after it changes
var imgHeight = $('#mainImg').height();
var currImg = 0;
var imagesSet = ["1.jpg", "2.jpg", "3.jpg", "4.jpg", "5.jpg", "6.jpg"];
var imageLoc = "images/zalman/"
$('#bttnRight').click(function(){
nextImg();
imgHeight = $('#mainImg').height();
resizeBttn();
});
function nextImg(){
currImg++;
if(currImg>=imagesSet.length){
currImg=0;
}
$('#mainImg').attr("src",imageLoc + imagesSet[currImg]);
}
function resizeBttn() {
$(document).ready(function() {
$('#bttnLeft').css("height",imgHeight);
$('#bttnLeft').css("bottom",imgHeight/2-5);
});
$(document).ready(function() {
$('#bttnRight').css("height",imgHeight);
$('#bttnRight').css("bottom",imgHeight/2-5);
});
}
for some reason, it doesn't always grab the height at the correct time and the side bars will stay at the previous height.
Below I have a JSFiddle that should be working the way my setup is.
Please excuse any inconsistencies and inefficiencies, I am learning.
Just seems weird that it would sometimes grab the height and sometimes not.
I will also be attaching an image of what I see sometimes from the JSfiddle.
I will also attach an image of what I see on my site I am actually writing.
https://jsfiddle.net/6bewkuo5/6/
The issue is because your JavaScript accessing the height of the image before the image as actually been re-rendered in the DOM. Adding a slight delay after assigning the new image source may help things, but...
You actually don't need to use JavaScript to set the height of the buttons
You can achieve what you're after by placing the buttons and image inside of a container with css attribute display: flex.
Like this:
.container {
display: flex;
justify-content: center;
}
<div class="container">
<button class="prev"><</button>
<img src="https://www.avalonwinery.com/wp-content/uploads/2013/12/200x300.gif">
<button class="next">></button>
</div>
Elements within a flex container will automatically fill the height, this includes buttons. Because the images will automatically adjust the height of the container, the buttons will also automatically adjust their height to match.
Run the example below
const images = [
"https://www.avalonwinery.com/wp-content/uploads/2013/12/200x300.gif",
"https://images.sftcdn.net/images/t_app-logo-xl,f_auto/p/ce2ece60-9b32-11e6-95ab-00163ed833e7/1578981868/the-test-fun-for-friends-logo.png",
"https://hiveconnect.co.za/wp-content/uploads/2018/05/800x600.png",
"https://upload.wikimedia.org/wikipedia/commons/b/b5/800x600_Wallpaper_Blue_Sky.png"
]
const imageEl = document.querySelector('img')
let imageIndex = 0
document.querySelector('.prev').addEventListener('click', e => {
if (--imageIndex < 0) { imageIndex = images.length - 1 }
imageEl.src = images[imageIndex]
})
document.querySelector('.next').addEventListener('click', e => {
if (++imageIndex > images.length - 1) { imageIndex = 0 }
imageEl.src = images[imageIndex]
})
body {
background-color: #206a5d;
color: #ffffff;
text-align: center;
}
.container {
display: flex;
justify-content: center;
}
img {
max-width: 50%;
}
<h1>Zalman Build</h1>
<div class="container">
<button class="prev"><</button>
<img src="https://www.avalonwinery.com/wp-content/uploads/2013/12/200x300.gif">
<button class="next">></button>
</div>
The reason is because the resizeBttn code is firing before the image has actually finished downloading and loading into the DOM. I made these changes in your fiddle:
var imgHeight = $('#mainImg').height();
var currImg = 0;
var imagesSet = ["https://www.avalonwinery.com/wp-content/uploads/2013/12/200x300.gif","https://images.sftcdn.net/images/t_app-logo-xl,f_auto/p/ce2ece60-9b32-11e6-95ab-00163ed833e7/1578981868/the-test-fun-for-friends-logo.png", "https://hiveconnect.co.za/wp-content/uploads/2018/05/800x600.png", "https://upload.wikimedia.org/wikipedia/commons/b/b5/800x600_Wallpaper_Blue_Sky.png"];
var imageLoc = "images/zalman/"
$(document).ready(function() {
resizeBttn();
});
$( window ).resize(function() {
/* imgHeight = $('#mainImg').height() */; // commented out; we do this in resizeBttn now
resizeBttn();
});
$('#bttnLeft').click(function(){
prevImg();
/* imgHeight = $('#mainImg').height() */; // commented out; we do this in resizeBttn now
/* resizeBttn() */; // we do this as an `onload` to the image now
});
$('#bttnRight').click(function(){
nextImg();
/* imgHeight = $('#mainImg').height() */; // commented out; we do this in resizeBttn now
/* resizeBttn() */; // we do this as an `onload` to the image now
});
function nextImg(){
currImg++;
if(currImg>=imagesSet.length){
currImg=0;
}
$('#mainImg').attr("src",imagesSet[currImg]);
}
function prevImg(){
currImg--;
if(currImg<0){
currImg=imagesSet.length-1;
}
$('#mainImg').attr("src",imagesSet[currImg]);
}
function resizeBttn() {
imgHeight = $('#mainImg').height()
// removed superfluous doc.ready
$('#bttnLeft').css("height",imgHeight);
$('#bttnLeft').css("bottom",imgHeight/2-5);
$('#bttnRight').css("height",imgHeight);
$('#bttnRight').css("bottom",imgHeight/2-5);
}
And then rewrote your <img /> tag to call resizeBttn on onload:
<img id="mainImg" src="https://www.avalonwinery.com/wp-content/uploads/2013/12/200x300.gif" onload="resizeBttn()"/>
You can see this in action in this fiddle.
Also, a few additional notes on your code, at a glance:
You have some invalid HTML; you're going to want to run that through an HTML validator and fix it, because sometimes it is fine, but sometimes it can lead to all sorts of strange behavior.
You're playing fast and l0ose with global variables in your JS that get set in different functions; it might work OK while the script is small, but as things scale it can quickly become difficult to maintain
You should really avoid abusing the onclick to get link-like behavior from <li> elements; it can impact SEO as well as accessibility. I'd recommend simply using an anchor element inside or outside the <li>
I'd recommend taking a close look at this answer by user camaulay; he makes an excellent point that this may not require JS at all- if a more elegant solution exists w/ CSS it is probably going to be more performant and maintainable.

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.

jQuery Click event works only once (none of the previous Stack Overflow answers helped)

What I'm trying to do is that when the page loads I'm resetting an image to my desired small size.
If the user clicks on the image later it should enlarge with an animation, I'm done up to this part.
When the user again clicks on that image it should be resized to the size that I assigned after loading the page, I have tried toggle event, but that's not working, toggle just makes my images disappear from the page. So I created an alternate to toggle event by using if and else condition and a flag variable called "small" but the problem is that click event is working only once i.e: If the image is in the small size and I click on it, the image gets enlarged but when I click on it again the click event is fired but it doesn't work, I wish if there is any way that I could make it work with toggle event, otherwise I would like to do it by using if and else condition in click event.
Here's the HTML:
<html>
<head>
<title></title>
<script src="jquery-1.10.1.min.js"></script>
<script src="index.js"></script>
</head>
<body>
<img src="wordpress.jpg" class="small-Img" id="test"> <br>
<img src="store.jpg" class="small-Img">
</body>
</html>
Here's the script:
$(document).ready(function() {
var small;
$('.small-Img').on('load',function(){
$(".small-Img").attr('width','200');
small=Number(1);
});
$('.small-Img').on('click',function () {
alert("event fired");
if(small==1){
var obj=$(this);
var originalWidth=obj[0].naturalWidth;
var originalHeight=obj[0].naturalHeight;
$(this).animate({ height: originalHeight, width: originalWidth }, 1000, function() { });
small=Number(0);
}
if(small==0){
$(".small-Img").attr('width','200');
small=Number(1);
}
});
});
Your code
$(".small-Img").attr('width','200');
sets a width attribute on the image, similar to <img src="url" width="200"> which probably doesn't result in a size change. Try
$('.small-Img').css('width','200px');
or animate the shrinking
$('.small-Img').animate({ width: '200px' }, 1000);
You may also get better results making small an attribute of your image rather than a property of the window object
jsfiddle
It sounds like the problem isn't that the event isn't fired multiple times, but that it doesn't enter your if statement. Try making small a boolean variable instead of a number, that way you can avoid all the == vs === messyness
EDIT:
Also, you probably want an else if so that it doesn't shrink once it enlarges on each click.
DEMO
for setting or getting css value we use .css() not .attr()
== only checks the value
=== checks the value and the datatype
$(document).ready(function () {
var small;
$('.small-Img').on('load', function () {
$(".small-Img").css('width', '200'); //changed attr to css
small = 1;
});
$('.small-Img').on('click', function () {
if (small === 1) { //changed == to ===
var obj = $(this);
var originalWidth = obj[0].naturalWidth;
var originalHeight = obj[0].naturalHeight;
$(this).animate({
height: originalHeight,
width: originalWidth
}, 1000, function () {});
small = 0;
}
if (small === 0) { //changed == to ===
$(".small-Img").css('width', '200'); //changed attr to css
small = 1;
}
});
});
How about first making "small" a data-attribute on the image itself? Not a big deal, but a little more convenient (IMHO). The next thing is, when you want to check the second click, you might consider doing an else if rather than just an if. Not sure if it makes a difference, but it is a clear logical differentiation, you can have one or the other -- not both. Third, if you animate the width back down, you might also animate the height, calculated by your small height divided by your original height times the original width. Seems to work, see it here: http://jsfiddle.net/snowMonkey/7nCMF/1/
$('.small-Img').css('width','200px').data("small", 1);
$('.small-Img').on('click',function () {
var that=this;
this.smallWidth = "200px";
this.smallHeight = (200/$(this)[0].naturalWidth) * $(this)[0].naturalHeight+"px";
if($(this).data("small")===1 ){
var obj=$(that);
var originalWidth=obj[0].naturalWidth;
var originalHeight=obj[0].naturalHeight;
$(that).animate({
height: originalHeight,
width: originalWidth
}, 1000, function() { });
$(that).data("small",0);
} else if($(this).data("small")===0){
$(that).animate({
width: that.smallWidth,
height: that.smallHeight
}, 1000, function(){}).data("small", 1);
}
});
Best of luck!

scrollHeight not resetting after programmatically changing content

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.

Categories

Resources