I'm trying to modify an elements height (elementTwo) using another element's height (elementOne) using JS (No jQuery)
When I try logging elementOne.style.height, I get an empty string
Using Safari and Chrome inspector's I can see the computed height but cannot access it using JS. It shows up as faded (See screenshots)
Screenshot of Safari inspector |
Screenshot of Chrome inspector
.elementOne {
min-height: 100vh;
width:100%;
}
ElementOne has a min-height set to be 100vh but automatically increases in size based on the device / size of child elements. It does not have a height set. ElementTwo does not have any css by default
I am trying to do this using JS only and do not want to use jQuery at all.
If you want the height style for the element, you need to get the computed style with getComputedStyle (or currentStyle on old IE):
function getStyle(element) {
if (typeof getComputedStyle !== "undefined") {
return getComputedStyle(element);
}
return element.currentStyle; // Old IE
}
var heightStyle = getStyle(element).height;
That will be a string, with units on it (not necessarily the units in the CSS).
If you want the element's height, as opposed to its height style, use element.offsetHeight or element.clientHeight. They will be numbers; it's the number of pixels rounded to the nearest whole number.
If you want the most precise information you can get (including a fractional number of pixels if relevant; that can happen), you'd use getBoundingClientRect.
Example:
function getStyle(element) {
if (typeof getComputedStyle !== "undefined") {
return getComputedStyle(element);
}
return element.currentStyle; // Old IE
}
var element = document.querySelector(".foo");
var bounding = element.getBoundingClientRect();
console.log({
"height style": getStyle(element).height,
"offsetHeight": element.offsetHeight,
"clientHeight": element.clientHeight,
"bounding height": bounding.height
});
.container {
display: inline-block;
width: 100px;
height: 200px;
position: relative;
background-color: yellow;
}
.foo {
display: inline-block;
width: 100%;
height: 33.3%;
background-color: blue;
}
<div class="container">
<div class="foo"></div>
</div>
I think you want to use the property .clientHeight of the dom-element.
See the mdn arcticle about it.
Related
I am trying to write some JavaScript code that scales a div element using the transform:scale() CSS property, and then un-scales it so that the element returns to its original size. I thought that if I scale the element by applying transform:scale(a), I could un-scale it by applying transform:scale(1/a), since (element x a) x (1 / a) = element. However, that does not seem to work. Why not?
function scale_up() {
document.getElementById("div").style.transform = "scale(3)";
}
function scale_down() {
document.getElementById("div").style.transform = "scale(0.33)";
}
setTimeout(scale_up, 3000)
setTimeout(scale_down, 6000) /* this should return the div to its original size, but it doesn't */
#div {
height: 30px;
width: 30px;
border-style: solid;
}
<body>
<div id="div"></div>
</body>
CSS is a declarative language, and thus applying new properties on a given element or selector replaces existing ones with the same property name; properties are not added or multiplied when you set them again.
CSS transforms, building upon this, work the same way: setting the transform to scale(3) and then setting it to scale(0.33) has the same effect as just setting it to scale(0.33): the latter transform replaces the former.
Applying this principle, to undo the transform you can simply remove the CSS property that applies it to your element; you can do this by simply setting the property to an empty string, as per this StackOverflow answer. Alternatively, in this case, you can simply set a scale of 1:
function scale_up() {
document.getElementById("div").style.transform = "scale(3)";
}
function scale_down() {
document.getElementById("div").style.transform = "";
// This would also work:
//document.getElementById("div").style.transform = "scale(1)";
}
setTimeout(scale_up, 3000)
setTimeout(scale_down, 6000)
#div {
height: 30px;
width: 30px;
border-style: solid;
}
<body>
<div id="div"></div>
</body>
Applying a css rule doesn't add up, it replace each other.
So first, your scale_up apply a transform: scale(3), your element is scaled x3 from his original size.
Then your scale_down apply a transform: scale(0.33), your element is scaled /3 from his original size.
To set it back as normal, apply a transform: scale(1);
scale doesn't apply any permanent effects, you only need to revert to the default scale (100%):
document.getElementById("div").style.transform = "scale(1)";
Unfortunately 100vh is not always the same as 100% browser height as can be shown in the following example.
html,
body {
height: 100%;
}
body {
overflow: scroll;
}
.vh {
background-color: blue;
float: left;
height: 50vh;
width: 100px;
}
.pc {
background-color: green;
float: left;
height: 50%;
width: 100px;
}
<div class="vh"></div>
<div class="pc"></div>
The issue is more pronounced on iPhone 6+ with how the upper location bar and lower navigation bar expand and contract on scroll, but are not included in the calculation for 100vh.
The actual value of 100% height can be acquired by using window.innerHeight in JS.
Is there a convenient way to calculate the current conversion of 100vh to pixels in JS?
I'm trying to avoid needing to generate dummy elements with inline styles just to calculate 100vh.
For purposes of this question, assume a hostile environment where max-width or max-height may be producing incorrect values, and there isn't an existing element with 100vh anywhere on the page. Basically, assume that anything that can go wrong has with the exception of native browser functions, which are guaranteed to be clean.
The best I've come up with so far is:
function vh() {
var div,
h;
div = document.createElement('div');
div.style.height = '100vh';
div.style.maxHeight = 'none';
div.style.boxSizing = 'content-box';
document.body.appendChild(div);
h = div.clientHeight;
document.body.removeChild(div);
return h;
}
but it seems far too verbose for calculating the current value for 100vh, and I'm not sure if there are other issues with it.
How about:
function viewportToPixels(value) {
var parts = value.match(/([0-9\.]+)(vh|vw)/)
var q = Number(parts[1])
var side = window[['innerHeight', 'innerWidth'][['vh', 'vw'].indexOf(parts[2])]]
return side * (q/100)
}
Usage:
viewportToPixels('100vh') // window.innerHeight
viewportToPixels('50vw') // window.innerWidth / 2
The difference comes from the scrollbar scrollbar.
You'll need to add the height of the scrollbar to the window.innerHeight. There doesn't seem to be a super solid way of doing this, per this other question:
Getting scroll bar width using JavaScript
I have 2 divs, a navigation and a main content in a bootstrap grid system. The length of either can vary depending on amount of content. I need them both styled to fill 100% of the browser window IF neither has the content to reach the bottom naturally. But if at least one of the divs has more content than the length of the browser window, I need to be able to scroll down the page with the styles of both divs remaining in tact and both have a height of the longer of the 2 divs.
I'm currently using a javascript resize function which seems to work but not in the case where neither div is long enough to fill the height of the browser window. Any suggestions?
HTML
<div class="row">
<div id="nav" class="col-xs-2">
Variable Height Navigation
</div>
<div id="main" class="col-xs-10">
Variable Height Content
</div>
</div>
Javascript
function resize() {
var h = (document.height !== undefined) ? document.height : document.body.offsetHeight;
document.getElementById("nav").style.height = h + "px";
}
resize();
window.onresize = function () {
resize();
};
I am trying to understand you question, and if I'm correct what you are looking for is:
Both divs need to be equally high
They need be at least the height of the screen
They need to take the height of the highest div
So let's try to achieve this goal as simply as possible:
var main = document.getElementById('main');
var nav = document.getElementById('nav');
function resize(){
var highest;
// Set the divs back to autosize, so we can measure their content height correctly.
main.style.height = 'auto';
nav.style.height = 'auto';
// Find the highest div and store its height.
highest = main.clientHeight > nav.clientHeight
? main.clientHeight
: nav.clientHeight;
// Check if the highest value is the div or the window.
highest = highest > window.innerHeight
? highest
: window.innerHeight;
// Assign the newly found value
main.style.height = highest + 'px';
nav.style.height = highest + 'px';
}
resize();
// Also, you don't need to wrap it in a function.
window.resize = resize;
// However, better would be:
window.addEventListener('resize', resize, false);
#main, #nav {
width: 100%;
height: auto;
}
#main { background: red; }
#nav { background: green; }
<div id="main"></div>
<div id="nav"></div>
Now, If you aren't bothered with the actual sameness in heiught of both divs but just want them to at least be one screenful, you should consider using CSS:
html, body { height: 100%; }
#nav, #main { min-height: 100%; }
I think that is the better solution (no Javascript!) and sort-of does what you want, bar the fact that you won't have to equally high div elements. However, you would barely notice it as each will at least fill the page.
You could try using viewport height:
For example:
#nav {
min-height: 100vh;
}
#main {
min-height: 100vh;
}
See Bootply.
This will also remove the need for JavaScript.
The trouble is that I need these images with generated IDs "cam_snap_XXX" to become a different width if they are dragged and dropped into this area. I can make the height change but NOT THE WIDTH because the width is designated to 20px if x==1. Never is the image height specified therefore I believe that is the reason it is changeable? Q: How can I make these image widths change from 20px to 100px if "dragged"?
while (cnt <= 100) {
cam_icon=document.getElementById('cam_snap_' + cnt);
cam_icon.style.visibility = 'hidden';
if (x==1) {
cam_icon.style.width = '20px';
cam_icon.style.visibility = 'visible';
}
cnt++;
}
The height changes from the following javascript...
//js for adding class "dragged" which gives new height and width image parameters
$('.droparea td.drop').droppable({
onDrag: function (e, source) {
$(this).addClass('dragged'); //Changes height correctly not width though.
}
}
And the css..
//css for attempting to change image width and height on drop
.dragged{
height: 100px; //Works because height stretches image height from ~20px to 100px.
width: 100px; //**Doesn't work and is useless because width remains 20px.**
}
Should I try removing the class before adding class 'dragged' to these images? Using removeClass()? Any ideas are welcome even if not the solution.
This part of javascript modifies your HTML
cam_icon.style.width = '20px';
cam_icon.style.visibility = 'visible';
to
<someTag id="cam_snap_x" style="width:20px;visibility:visible">
According to css rules inline style (inside an HTML element) has the highest priority. so you'll have to change attribute value to see changes in UI.
Solution 1: change your onDrag function like below.
$('.droparea td.drop').droppable({
onDrag: function (e, source) {
$(this).width(100).height(100); //This will change the style property
}
}
Solution 2: Use !important to prioritize value specified in class over inline style attribute
.dragged{
height: 100px;
width: 100px !important;
}
I need a better way to calculate a scrollable div's viewport.
Under normal circumstances, I would use the following attributes: (scrollLeft, scrollTop, clientWidth, clientHeight)
Using these numbers I can accurately determine which part of a scrollable DOM element's viewport is currently visible, I use this information to asynchronously load things that are visible to the user on demand when scrolling to the content horizontally or vertically. When the content of the DIV is massive, this will avoid an embarassing browser crashing bug because of too many DOM elements being loaded.
My component has worked for a while now with no issues, this build we are introducing RTL support. Now everything is thrown off because of browser inconsistencies.
To demonstrate, I have created a simple example which will output the scrollLeft attribute of a scrollable element in a JSFiddle.
The behavior of the scrollLeft attribute on this simple scrollable element is not consistent from one browser to the next. The 3 major browsers I've tried all behaved differently.
FF-latest scrollLeft starts at 0 and goes negative when scrolling left
IE 9 scrollLeft starts at 0 and goes positive when scrolling left
Chrome-latest scrollLeft starts at a higher number and goes to 0 when scrolling left
I want to avoid having code like if(ie){...}else if(ff){...}else if (chrome){...} that would be horrible, and not maintainable in the long run in case browsers change behavior.
Is there a better way to figure out precisely which part of the DIV is currently visible?
Perhaps there is some other reliable DOM attribute other than scrollLeft?
Maybe there is a jQuery plugin that will do it for me, keeping in mind which browser version it is?
Maybe there is a technique I can use to figure out which of the cases it is at runtime without relying on some unreliable browser detection (i.e. userAgent)
Fiddle Example (code copied below)
HTML
<div id="box"><div id="content">scroll me</div></div>
<div id="output">Scroll Left: <span id="scrollLeft"></span></div>
CSS
#box {
width: 100px; height: 100px;
overflow: auto;
direction: rtl;
}
#content { width: 300px; height: 300px; }
JS
function updateScroll() {
$('#scrollLeft').text(box.scrollLeft());
}
var box = $('#box').scroll(updateScroll);
updateScroll();
Here's a jQuery plugin which does not use browser detection: https://github.com/othree/jquery.rtl-scroll-type
Using this plugin you could replace jQuery's scrollLeft function with your own predictable version, like this:
var origScrollLeft = jQuery.fn.scrollLeft;
jQuery.fn.scrollLeft = function(i) {
var value = origScrollLeft.apply(this, arguments);
if (i === undefined) {
switch(jQuery.support.rtlScrollType) {
case "negative":
return value + this[0].scrollWidth - this[0].clientWidth;
case "reverse":
return this[0].scrollWidth - value - this[0].clientWidth;
}
}
return value;
};
I didn't include the code for setting the scroll offset, but you get the idea.
Here's the fiddle: http://jsfiddle.net/scA63/
Also, this lib may be of interest too.
You can try this:-
var initialScrollLeft = $('#box').scrollLeft(), negativeToZero, startFromZero;
if(initialScrollLeft === 0){
startFromZero = true;
} else if(initialScrollLeft < 0){
negativeToZero = true;
}
var box = $('#box').scroll(function(){
if(startFromZero){
if(box.scrollLeft()>0){
$('#scrollLeft').text(- (box.scrollLeft()));
}else {
$('#scrollLeft').text(box.scrollLeft());
}
} else if(negativeToZero){
$('#scrollLeft').text(box.scrollLeft()+(box[0].scrollWidth - box[0].clientWidth));
} else{
$('#scrollLeft').text(box.scrollLeft()-(box[0].scrollWidth - box[0].clientWidth));
}
});
Problem: (Ex. Scroll Width = 100)
Chrome - Most Right: 100 Most Left: 0.
IE- Most Right: 0 Most Left: 100.
Firefox - Most Right: 0 Most Left: -100.
Solution #1
As mentioned by #Lucas Trzesniewski.
You could use this Jquery plugin:
https://github.com/othree/jquery.rtl-scroll-type
The plugin is used to detect which type is the browser are using.
Assign the result to jQuery's support object named 'rtlScrollType'.
You will need the scrollWidth of the element to transform between
these three types of value
Solution #2
Credits: jQuery.scrollLeft() when direction is rtl - different values in different browsers
I know you didn't want to include browser detection individually for each browser. With this example, only 2 extra lines of code are added for Safari and Chrome and it works like a charm!
Modified it to demonstrate it better for you.
$('div.Container').scroll(function () {
st = $("div.Container").scrollLeft() + ' ' + GetScrollLeft($("div.Container"));
$('#scrollLeft').html(st);
});
function GetScrollLeft(elem) {
var scrollLeft = elem.scrollLeft();
if ($("body").css("direction").toLowerCase() == "rtl") {
// Absolute value - gets IE and FF to return the same values
var scrollLeft = Math.abs(scrollLeft);
// Get Chrome and Safari to return the same value as well
if ($.browser.webkit) {
scrollLeft = elem[0].scrollWidth - elem[0].clientWidth - scrollLeft;
}
}
return scrollLeft;
}
JSFiddle:
http://jsfiddle.net/SSZRd/1/
The value on the left should be the same for all browser while the value on the right is the older value which is different on all browser. (Tested on Firefox, Safari, Chrome, IE9).
1. FF-latest scrollLeft starts at 0 and goes negative when scrolling left
2. IE 9 scrollLeft starts at 0 and goes positive when scrolling left
3. Chrome-latest scrollLeft starts at a higher number and goes to when scrolling left
I want to avoid having code like if(ie){...}else if(ff){...}else if(chrome){...}
that would be horrible, and not maintainable in the long run in case browsers change behavior
FYI:
Chrome 85 (final shipping Aug. 2020) fixed this bug and aligns behaviour with Firefox and Safari and the spec.
See https://www.chromestatus.com/feature/5759578031521792
Is there a feature detection available for this?
Yes, e.g. use one of two scrips (from Frédéric Wang) available here:
https://people.igalia.com/fwang/scrollable-elements-in-non-default-writing-modes/
either this
function scroll_coordinates_behavior_with_scrollIntoView() {
/* Append a RTL scrollable 1px square containing two 1px-wide descendants on
the same line, reveal each of them successively and compare their
scrollLeft coordinates. The scrollable square has 'position: fixed' so
that scrollIntoView() calls don't scroll the viewport. */
document.body.insertAdjacentHTML("beforeend", "<div style='direction: rtl;\
position: fixed; left: 0; top: 0; overflow: hidden; width: 1px; height: 1px;'>\
<div style='width: 2px; height: 1px;'><div style='display: inline-block;\
width: 1px;'></div><div style='display: inline-block; width: 1px;'></div>\
3</div></div>");
var scroller = document.body.lastElementChild;
scroller.firstElementChild.children[0].scrollIntoView();
var right = scroller.scrollLeft;
scroller.firstElementChild.children[1].scrollIntoView();
var left = scroller.scrollLeft;
/* Per the CSSOM specification, the standard behavior is:
- decreasing coordinates when scrolling leftward.
- nonpositive coordinates for scroller with leftward overflow. */
var result = { "decreasing": left < right, "nonpositive": left < 0 };
document.body.removeChild(scroller);
return result;
}
or that
function scroll_coordinates_behavior_by_setting_nonpositive_scrollLeft() {
/* Append a RTL scrollable 1px square containing a 2px-wide child and check
the initial scrollLeft and whether it's possible to set a negative one.*/
document.body.insertAdjacentHTML("beforeend", "<div style='direction: rtl;\
position: absolute; left: 0; top: 0; overflow: hidden; width: 1px;\
height: 1px;'><div style='width: 2px; height: 1px;'></div></div>");
var scroller = document.body.lastElementChild;
var initially_positive = scroller.scrollLeft > 0;
scroller.scrollLeft = -1;
var has_negative = scroller.scrollLeft < 0;
/* Per the CSSOM specification, the standard behavio999r is:
- decreasing coordinates when scrolling leftward.
- nonpositive coordinates for scroller with leftward overflow. */
var result = { "decreasing": has_negative ||
initially_positive, "nonpositive": has_negative };
document.body.removeChild(scroller);
return result;
}