Find element height, including margin - javascript

I'm making a simple and fun drawing app, and in it the structure is as follows:
Header
Canvas
Footer
I'm trying to get the canvas to be the full height of the window, minus the header height and footer height.
I've tried multiple things like:
canvas.height = window.height - (document.getElementById("header").offsetHeight + document.getElementById("footer").offsetHeight);
And I've even tried:
function getHeight(id) {
id = document.getElementById(id);
return id.offsetHeight + id.style.marginTop + id.style.marginBottom;
}
canvas.height = window.height - (getHeight("header") + getHeight("footer"))
But to no avail. In the console id.style.marginTop just returned an empty string, although its marginTop is set in the css... just not set specifically to marginTop, it's set as margin: 8px 0 8px 0;
Is there any way, without using jQuery, to obtain the rendered height of an element, including margin?
I'm assuming we'd have to separate the margin: 8px 0 8px 0; into separate variables, using id.style.margin... but I'm just not sure how to separate it.

function outerWidth(el) {
var width = el.offsetWidth;
var style = getComputedStyle(el);
width += parseInt(style.marginLeft) + parseInt(style.marginRight);
return width;
}
this does exactly what jquery's outerwidth is doing, scratched from ::http://youmightnotneedjquery.com/
might be interteresting for further develpoment
EDIT: in ie8 you have to use el.currentStyle[prop] instead of getComputedStyle(el);

You could use a flex solution and not worry about calculating the height:
CSS:
.container {
display: flex;
flex-direction: column;
height: 100%;
}
canvas {
flex: 1;
}
HTML:
<div class="container">
<header>...</header>
<canvas>...</canvas>
<footer>...</footer>
</div>
Fiddle

Related

How can I make the canvas item to fill the browser?

I set the canvas's size in js:
const cv = document.getElementById("cv");
cv.width = innerWidth;
cv.height = innerHeight;
However, there are scroll bars apeared.
How can I deal with it? (without using overflow: hidden)
Using overflow: hidden is the correct and easiest code to place in your CSS.
#cv {
overflow: hidden;
}
This will remove the scrollbars.
However, if you want to avoid that code, you can set padding and margin to 0, which will more or less have the same effect.
#cv {
padding: 0;
margin: 0;
}
This will also remove the scrollbars, kind of like overflow: hidden.

div position based on scroll position

I would like my logo to scroll up and down vertically based on the scroll position on the website.
In exactly the same way a default scroll bar indicates your position on the site, I would like my logo to do the same. When you are at the top of the website page, the logo sits at the top, and when you are at the bottom it will sit at the bottom of the page in a vertical bar on the left hand side of the web page.
I have no idea how to approach this, I have looked at a few plugins but none offer the positioning based on the content and I can't find any other Stack Overflow results that are what I am looking for, though I may not be phrasing the question correctly.
My setup is
.logo-scroll {
position: fixed;
border: 2px solid white;
top: 30px;
left: 30px;
height: calc(100vh - 60px);
width: 75px;
}
.scroll-text {
height: auto;
}
.logo-scroll .scroll-text img {
padding: 0 6px 0 17px;
}
and my html
<div class="logo-scroll">
<div class="scroll-text">
<a href="/home">
<img src="logo.svg"/>
</a>
</div>
</div>
Any help would be greatly appreciated
** Edit - to complicate things, I have a 30px border which is not to be included in the page height. So an offset of 30px on the top and bottom.
The size of the margin/border will need to change responsively at break points - 1 maybe 2, before I will hide it. Essentially the height of the scroll bar will always need to match the height of either the page with margins subtracted or the height of the :before element.
Alternatively if I can set offsets, I can reuse the JS and adjust based on screen size. Like media queries for JS?
You can see the web page here - which is still very under construction https://www.sheree-new.shereewalker.com/
You could give something like this a try.
window.addEventListener('scroll', e => {
const logo = document.querySelector('.scroll-text');
const logoHeight = logo.clientHeight;
const viewHeight = window.innerHeight;
const maxLogoOffset = viewHeight - logoHeight;
const scrollFraction = getElementScrollFraction(document.querySelector('body'));
logo.style.top = maxLogoOffset*scrollFraction;
});
function getElementScrollFraction(elem){
return elem.scrollTop / (elem.scrollHeight - elem.clientHeight);
}
You'll also need to add position:fixed; to the .scroll-text css.
Here is a working example: https://jsbin.com/yuholihece/edit?html,css,js,console,output
Here is my solution.
Edited:
const docHeight = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight);
const logo = document.querySelector('.scroll-text');
const logoHeight = logo.offsetHeight;
// to get the pseudoelement's '#page::before' top we use getComputedStyle method
const barTopMargin = parseInt(getComputedStyle(document.querySelector('#page'), '::before').top);
let viewportHeight, barHeight, maxScrollDist, currentScrollPos, scrollFraction;
logo.style.top = barTopMargin + 'px';
window.addEventListener('load', update);
window.addEventListener('resize', setSizes);
document.addEventListener('scroll', update);
setSizes();
function update() {
currentScrollPos = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
scrollFraction = currentScrollPos / (docHeight - viewportHeight);
logo.style.top = barTopMargin + (scrollFraction * maxScrollDist) + 'px';
}
function setSizes() {
viewportHeight = window.innerHeight;
// to get the pseudoelement's '#page::before' height we use getComputedStyle method
barHeight = parseInt(getComputedStyle(document.querySelector('#page'), '::before').height);
maxScrollDist = barHeight - logoHeight;
update();
}
And if I understand correctly, you want #page::before element to have like margins on its top, left, bottom and right. If so, then I think it would be better to use this styling rules:
#page::before {
content: "";
position: fixed;
top: 30px;
bottom: 30px;
left: 30px;
right: 30px;
...
}
When you use position: fixed property (or position: absolute), you can stretch the element's width and height as you want by just setting top - bottom and left - right properties at the same time.
P. S.: And also there is no sense in using display: inline-block, because position: fixed (and position: absolute) automatically sets display to block :)
Hopefully, this helps you!

jQuery CSS method update on resize?

After struggling to vertically centre a div inside the body element using the "conventional" methods, I've decided to create a small jQuery function that figures out how far from the top an element needs to be to be "centred".
It works like this:
Get container height,
Get child height,
"top" = "(container.height - child.height) / 2"
Set margin top of child to the value of "top".
For example if the body had a width and height of 1000px and this body had a div.inner child that had a width and height of 400px the margin-top of div.inner would be 300px because (1000-400) / 2 = 300.
Here is a diagram to further explain what I mean:
NOTE: X represents the margin-top of the div.inner (as I didn't have enough space for "Margin Top = ").
To my amazement this actually works!!! Here is the test code:
// set the margin top for ".vertical-centre" elements
$(".vertical-centre").each(function() {
// set the margin-top for the child
$(this).css("margin-top", function() {
// NOTE: margin = (container.height - child.height) / 2
var margin = ($(this).parent().height() - $(this).height()) / 2;
// default the margin to zero if it's a negative number
// round the margin down to the nearest whole number
// specify that the margin-top is in pixels
return Math.floor(Math.max(0, margin)) + "px";
});
});
body {
width: 100px;
height: 100px;
margin: 0;
padding: 0;
border: 1px solid black
}
div.inner {
width: 40px;
height: 40px;
background-color: blue
}
.horizontal-centre {
display: block;
margin-left: auto;
margin-right: auto
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="inner horizontal-centre vertical-centre"></div>
NOTE: I made the example above smaller so you could see it properly.
Unfortunately though, there is now another problem, when I resize the browser the margin-top of the div.inner element stays the same.
I would like for it to be responsive and update it's margin-top property to the appropriate value when the window has been resized otherwise div.inner will go out of view and the page will look a like this:
You could use https://api.jquery.com/resize/
Create a function of your code
function init_center() {..
Try calling the init_center function from the resize event of window
SNIPPET
function init_center() {
// set the margin top for ".vertical-centre" elements
$(".vertical-centre").each(function() {
// set the margin-top for the child
$(this).css("margin-top", function() {
// NOTE: margin = (container.height - child.height) / 2
var margin = ($(this).parent().height() - $(this).height()) / 2;
// default the margin to zero if it's a negative number
// round the margin down to the nearest whole number
// specify that the margin-top is in pixels
return Math.floor(Math.max(0, margin)) + "px";
});
});
}
$( window ).resize(init_center); // Handle resize of window
init_center(); // Doing it first time
body {
width: 400px;
height: 400px;
margin: 0;
padding: 0;
border: 1px solid black
}
div.inner {
width: 200px;
height: 200px;
background-color: blue
}
.horizontal-centre {
display: block;
margin-left: auto;
margin-right: auto
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="inner horizontal-centre vertical-centre"></div>
Wrap you code in a function
function align() {
$(".vertical-centre").each(function() {
// set the margin-top for the child
$(this).css("margin-top", function() {
// NOTE: margin = (container.height - child.height) / 2
var margin = ($(this).parent().height() - $(this).height()) / 2;
// default the margin to zero if it's a negative number
// round the margin down to the nearest whole number
// specify that the margin-top is in pixels
return Math.floor(Math.max(0, margin)) + "px";
});
});
}
And run it on window resize as well
align(); // first run
$(window).on('resize', align); // when window resize

Making a container grow on hover using jQuery

I'm trying to make a jQuery function that takes the current height and width of a link then makes it grow by whatever amount of px I set. I don't have much so far and I'm completely lost.
$('.absolute_img_links').hover(function(){
var link_height = $('.absolute_img_links').outerHeight();
var link_width = $('.absolute_img_links').width();
});
I know how to get the current height and width but I don't know how to basically say height of .absolute_img_links = link_height + 10px in jQuery
same functions do the job:
$('.absolute_img_links').hover(function(){
var link_height = $('.absolute_img_links').outerHeight();
$('.absolute_img_links').outerHeight(link_height + 10);
var link_width = $('.absolute_img_links').width();
$('.absolute_img_links').width(link_width + 10);
});
To achieve what you need you can pass a function to the height() and width() functions. In these functions you receive the current value, to which you can just add the 10px as required.
Note that hover() fires twice, once for mouseenter and once for mouseleave. Also, you don't reset the size of the element, so it just gets larger and larger on successive hovering. To fix this, amend your logic so that you attach the event handlers separately instead of one hover() call so you can increase/decrease the size as needed. Try this:
$('.absolute_img_links').on('mouseenter', function(){
$(this)
.height(function(i, height) { return height + 10; })
.width(function(i, width) { return width + 10; });
}).on('mouseleave', function() {
$(this)
.height(function(i, height) { return height - 10; })
.width(function(i, width) { return width - 10; });
});
.absolute_img_links {
position: absolute;
background-color: #C00;
color: #FFF;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="absolute_img_links">
Hover me!
</div>
Also note that you can implement a pure CSS version of this which has the exact same effect, although simply adds padding to the right and bottom of the element, instead of changing the width/height. Try this:
.absolute_img_links {
position: absolute;
background-color: #C00;
color: #FFF;
}
.absolute_img_links:hover {
padding: 0 10px 10px 0;
}
<div class="absolute_img_links">
Hover me!
</div>
Using the above method you could even keep the text centralised too, by adding a consistent 5px padding around the entire element.
Using height() and width() you can simply add 10 to the values you have found to increase the height/width of the container.
$('.absolute_img_links').hover(function() {
//var link_height = $('.absolute_img_links').outerHeight();
//var link_width = $('.absolute_img_links').width();
//Changed to $(this)
var link_height = $(this).outerHeight();
var link_width = $(this).width();
$(this).height(link_height + 10);
$(this).width(link_width + 10);
});
.absolute_img_links {
width: 300px;
height: 300px;
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="absolute_img_links"></div>
I know you asked for jQuery but...
This is trivial (and more flexible) in CSS:
.absolute_img_links img { transition: all .2s ease-in-out; }
.absolute_img_links:hover img { transform: scale(1.1); }
<img src="https://placeimg.com/100/100/any" />
<img src="https://placeimg.com/100/100/any" />

Getting true dimensions of images with jquery on collected dom elements

I am trying to get the dimensions of images on a page for further use with a custom 'lightbox' or sorts. However, when trying both a pure js method, and a jquery method, I get the output undefined on my variables. Why is this? Is it because of jquery load event? I tried both onload and ready.
Basically I need the full dimensions of the image to justify whether it should be loaded in a lightbox with a click event or not.
Update I am now able to get console feedback from the function now, however it's not providing me a dimension of the image.
$('.postbody').find('img').each(function() {
var img = new Image(), width, height;
$(img).load(function() {
width = $(this).width();
height = $(this).height();
console.log('Width: '+width+' Height: '+height);
});
console.log($(this).attr('src'));
img.src = $(this).attr('src');
});
#theater-box {
display: none;
position: fixed;
width: auto;
height: auto;
min-width: 1005px;
max-width: 1428px;
padding: 10px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0,0,0,0.90);
border: 2px solid rgba(255,255,255,0.4);
}
.postbody {
width: 90%;
height: 90%;
background: rgba(100,50,50,0.5);
}
.postbody * img {
width: 100%;
max-width: 1168px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="theater-box"></div>
<div class="postbody">
<div id="someclass"><img src="https://e8zzxa.bl3301.livefilestore.com/storageservice/passport/auth.aspx?sru=https:%2f%2fe8zzxa.bl3301.livefilestore.com%2fy2pDapooeiISgUV7-ugpyADuRULJ_stpkiALbypYJHjNxrhUqcvRsZ6eRk4PiJlClABLOfByjulDSDLOMCEpHhggVkgvM4z5Gdq0Jo-C0e1pCU%2fMajipoorHighlands2.jpg&wa=wsignin1.0" /></div>
</div>
You are setting the variables asynchronously and getting it directly.
In pseudocode it is a bit like this:
Set the function to retrieve the width and height when the images loads
Display the width and height variables (not set yet)
The functions set in step 1 runs and sets the varaibles.
So your code that uses the width and height should be inside the image.load function.
I hope it helps, if you have any further questions dont hesitate to comment :-)
Perhaps you can just put the console.log line as the last line in the $(img).load function.
Try this...
$(img).load = function() {
var $this = $(this);
width = $this.width();
height = $this.height();
}
I'm not exactly sure why the original method (which works in a lot of examples) was not working here. So I found some awesome code by GregL from right here at Stackoverflow.
Essentially, the method loads a new, and hidden image into the body, and then captures the width and height before removing it.
$('.postbody').find('img').each(function() {
var img = $(this), width, height,
hiddenImg = img.clone().css('visibility', 'hidden').removeAttr('height').removeAttr('width').appendTo('body');
width = hiddenImg.height();
height = hiddenImg.width();
hiddenImg.remove();
console.log('Width: '+width+' Height: '+height);
});
Check out the Fiddle

Categories