When an element contains inline-blocks which contain padding it doesn't get included in the width calculations of the element.
Essentially the same issue as jQuery outerWidth on a parent element which has child elements with padding.
This page should have text that lines up along the right side of the green box,
however the text will always grow larger than it's container, because width never includes the padding of any of it's children.
Is there a way to find the width of an element correctly without manually enumerating all child elements and re-adding the padding of each child? Same results when using .css('width'), .width() or .outerWidth().
<html>
<head>
<script src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
<script>
jQuery(document).ready(function() {
var e = jQuery('#BLAH');
var pw = e.parent().width();
e.css('font-size','1px');
if (e.outerWidth() < pw) {
while ( e.outerWidth() < pw) {
alert('width ' + e.outerWidth() + ' < ' + pw);
e.css('font-size','+=1px');
}
e.css('font-size','-=1px');
}
});
</script>
<style>
#BLAH {
background-color: red;
white-space: nowrap;
}
.BLAH {
//padding: 0 10%;
background-color: blue;
display: inline;
}
</style>
</head>
<body>
<div style="background-color: green; width: 50%; height: 50%">
<div id="BLAH" style="display: inline-block;">
<div class="BLAH">BLAH</div>
<div class="BLAH">BLAH</div>
<div class="BLAH">BLAH</div>
<div class="BLAH">BLAH</div>
</div>
</div>
</body>
</html>
As the issue only occurs with percentage padding, you can instead use fixed pixel padding and increase that along with your font-size in the javascript. Something like this:
jQuery(document).ready(function () {
var e = jQuery('#BLAH');
var pw = e.parent().width();
e.css('font-size', '1px');
if (e.outerWidth() < pw) {
while (e.outerWidth() < pw) {
console.log('width ' + e.outerWidth() + ' < ' + pw);
e.css('font-size', '+=1px');
jQuery(".BLAH").css({
'padding-right': '+=1px',
'padding-left': '+=1px'
});
}
e.css('font-size', '-=1px');
jQuery(".BLAH").css({
'padding-right': '-=1px',
'padding-left': '-=1px'
});
}
});
http://jsfiddle.net/5Gufk/
If you wanted to get more sophisticated with it, you could calculate the padding as a percentage of the parent element's previous width and apply that rather than just increasing by one, or any other formula.
As for why it works this way, I was unable to find the part of the CSS spec that defines this behavior. However, it is important to understand that a percentage padding is based on the width of the containing block. Consider how it would work if you had 6 elements, all with 10% padding on both sides. That would be 120% padding, how could that even be possible for the padding of the elements to be 120% of the width of the parent element and still fit inside the parent?
Related
I have a scrollable div container fits multiple "pages" (or div's) inside of it's container.
My goal is to, at any given moment, figure out where inside my red container does it reach the top of my scrollable container. So it can be a constant on scroll event, or a button that triggers this task.
So for example. If I have a absolute div element inside one of my red boxes at top:50px. And if I scroll to where that div element reaches the top of my scrollable container. The trigger should say that I am at 50px of my red container.
I'm having a hard time grasping how to accomplish this but I've tried things like:
$("#pageContent").scroll(function(e) {
console.log($(this).scrollTop());
});
But it doesn't take into account the separate pages and I don't believe it it completely accurate depending on the scale. Any guidance or help would be greatly appreciated.
Here is my code and a jsfiddle to better support my question.
Note: If necessary, I use scrollspy in my project so I could target which red container needs to be checked.
HTML
<div id="pageContent" class="slide" style="background-color: rgb(241, 242, 247); height: 465px;">
<div id="formBox" style="height: 9248.627450980393px;">
<div class="trimSpace" style="width: 1408px; height: 9248.627450980393px;">
<div id="formScale" style="width: 816px; -webkit-transform: scale(1.7254901960784315); display: block;">
<form action="#" id="XaoQjmc0L51z_form" autocomplete="off">
<div class="formContainer" style="width:816px;height:1056px" id="xzOwqphM4GGR_1">
<div class="formContent">
<div class="formBackground">
<div style="position:absolute;top:50px;left:100px;width:450px;height:25px;background-color:#fff;color:#000;">When this reaches the top, the "trigger" should say 50px"</div>
</div>
</div>
</div>
<div class="formContainer" style="width:816px;height:1056px" id="xzOwqphM4GGR_2">
<div class="formContent">
<div class="formBackground"><div style="position:absolute;top:50px;left:100px;width:450px;height:25px;background-color:#fff;color:#000;">This should still say 50px</div></div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
CSS
#pageContent {
position:relative;
padding-bottom:20px;
background-color:#fff;
z-index:2;
overflow:auto;
-webkit-overflow-scrolling: touch;
-webkit-transform: translate(0, 0);
-moz-transform: translate(0, 0);
-ms-transform: translate(0, 0);
transform: translate(0, 0);
}
#formBox {
display: block;
position: relative;
height: 100%;
padding: 15px;
}
.trimSpace {
display: block;
position: relative;
margin: 0 auto;
padding: 0;
height: 100%;
overflow: hidden;
}
#formScale::after {
display: block;
content:'';
padding-bottom:5px;
}
#formScale {
position:relative;
width:816px;
margin:0;
-webkit-transform-origin: 0 0;
-moz-transform-origin: 0 0;
transform-origin: 0 0;
-ms-transform-origin: 0 0;
}
.formContainer {
position:relative;
margin : 0 auto 15px auto;
padding:0;
}
.formContent {
position:absolute;
width:100%;
height:100%;
}
.formBackground {
width:100%;
height:100%;
background-color:red;
}
JS
var PAGEWIDTH = 816;
$(window).resize(function (e) {
zoomProject();
resize();
});
function resize() {
$("#pageContent").css('height', window.innerHeight - 45 + 'px');
}
function zoomProject() {
var maxWidth = $("#formBox").width(),
percent = maxWidth / PAGEWIDTH;
$("#formScale").css({
'transform': 'scale(' + percent + ')',
'-moz-transform': 'scale(' + percent + ')',
'-webkit-transform': 'scale(' + percent + ')',
'-ms-transform': 'scale(' + percent + ')'
});
$(".trimSpace").css('width', (PAGEWIDTH * percent) + 'px');
$("#formBox, .trimSpace").css('height', ($("#formScale").height() * percent) + 'px');
}
zoomProject();
resize();
EDIT:
I don't think I am conveying a good job at relaying what I want to accomplish.
At the moment there are two .formContainer's. When I scroll #pageContainer, the .formContainer divs move up through #pageContainer.
So what I want to accomplish is, when a user clicks the "ME" button or #click (as shown in the fiddle below), I'd like to know where in that particular .formContainer, is it touching the top of #pageContainer.
I do use scroll spy in my real world application so I know which .formContainer is closest to the top. So if you just want to target one .formContainer, that is fine.
I used these white div elements as an example. If I am scrolling #pageContainer, and that white div element is at the top of screen as I am scrolling and I click on "ME", the on click trigger should alert to me that .formContainer is touching the top of #pageContainer at 50px from the top. If, the the red container is just touching the top of #pageContainer, it should say it is 0px from the top.
I hope that helps clear up some misconception.
Here is an updated jsfiddle that shows the kind of action that I want to happen.
I am giving this a stab because I find these things interesting. It might just be a starting point since I have a headache today and am not thinking straight. I'd be willing to bet it can be cleaned up and simplified some.
I also might be over-complicating the approach I took, getting the first visible form, and the positioning. I didn't use the getBoundingClientRect function either.
Instead, I approached it trying to account for padding and margin, using a loop through parent objects up to the pageContent to get the offset relative to that element. Because the form is nested a couple levels deep inside the pageContent element you can't use position(). You also can't use offset() since that changes with scroll. The loop approach allowed me to factor the top margin/padding in. I haven't looked at the other solutions proposed fully so there might be a shorter way to accomplish this.
Keeping in mind that the scale will affect the ACTUAL location of the child elements, you have to divide by your scale percentage when getting the actual location. To do that I moved the scalePercentage to a global var so it was usable by the zoom function and the click.
Here's the core of what I did. The actual fiddle has more logging and junk:
var visForm = getVisibleForm();
var formTop = visForm.position().top;
var parents = visForm.parentsUntil('#pageContent');
var truOffset = 0;
parents.each(function() {
truOffset -= $(this).position().top;
});
// actual location of form relative to pageContent visible pane
var formLoc = truOffset - formTop;
var scaledLoc = formLoc / scalePercent;
Updated Fiddle (forgot to account for scale in get func): http://jsfiddle.net/e6vpq9c8/5/
If I understand your question correctly, what you want is to catch when certain descendant elements reach the top of the outer container, and then determine the position of the visible "page" (div with class formContainer) relative to the top.
If so, the first task is to mark the specific elements that could trigger this:
<div class='triggerElement' style="position:absolute;top:50px;left:100px;width:450px;height:25px;background-color:#fff;color:#000;">When this reaches the top, the "trigger" should say 50px"</div>
Then the code:
// arbitrary horizontal offset - customize for where your trigger elements are placed horizontally
var X_OFFSET = 100;
// determine once, at page load, where outer container is on the page
var outerContainerRect;
$(document).ready(function() {
outerContainerRect = $("#pageContent").get(0).getBoundingClientRect();
});
// when outer container is scrolled
$("#pageContent").scroll(function(e) {
// determine which element is at the top
var topElement = $(document.elementFromPoint(outerContainerRect.left+X_OFFSET, outerContainerRect.top));
/*
// if a trigger element
if (topElement.hasClass("triggerElement")) {
// get trigger element's position relative to page
console.log(topElement.position().top);
}
*/
var page = topElement.closest(".formContainer");
if (page.length > 0) {
console.log(-page.get(0).getBoundingClientRect().top);
}
});
EDIT: Changed code to check formContainer elements rather than descendant elements, as per your comment.
http://jsfiddle.net/j6ybgf58/23/
EDIT #2: A simpler approach, given that you know which formContainer to target:
$("#pageContent").scroll(function(e) {
console.log($(this).scrollTop() - $("#xzOwqphM4GGR_1").position().top);
});
http://jsfiddle.net/rL4Ly3yy/5/
However, it still gives different results based on the size of the window. This seems unavoidable - the zoomProject and resize functions are explicitly resizing the content, so you would have to apply the inverse transforms to the number you get from this code if you want it in the original coordinate system.
I do not fully understand what it is that you are needing, but if i am correct this should do the trick
$("#pageContent").scroll(function(e) {
// If more then 50 pixels from the top has been scrolled
// * if you want it to only happen at 50px, just execute this once by removing the scroll listener on pageContent
if((this.scrollHeight - this.scrollTop) < (this.scrollHeight - 50)) {
alert('it is');
}
});
ScrollHeight is the full height of the object including scrollable pixels.
ScrollTop is the amount of pixels scrolled from the top.
You can use waypoints to detect the position of divs based on where you're scrolling.
Here is a link to their official website's example: http://imakewebthings.com/waypoints/shortcuts/inview/
I am trying to scale(X) the child div element to fit the parent div on window resize. I calculated the scale ratio by dividing the parent width and child width, and it works perfectly.
But alignment is not proper. Initially i set my child div margin-left 50 px from the parent div. Its not maintained on the scale.
This is the HTML/css code
#wrap {
margin-left:50px;
width:300px;
height:300px
}
#parentDiv {
width:500px
height:300px;
}
<body>
<div id="parentDiv">
<div id="wrap">
<img id="image" width="300px" src="http://placekitten.com/640/480" />
</div>
</div>
</body>
And this is the js code to scale on resize
window.onresize = function() {
scaleDiv();
};
scaleDiv = function() {
parentWidth = $('#parentDiv').width();
scale = (parentWidth) / $('#wrap').width();
new_width = $('#wrap').width() * scale;
$('#wrap').css('-webkit-transform', ' scaleX(' + scale + ')');
}
After some googling, i came to know that i can use translateX property to keep the alignment as original. But i am struck at how to calculate the translate value.
$('#wrap').css('-webkit-transform', 'translateX(20%)' + ' scaleX(' + scale + ')');
i put random value 20% on translate prop, its not proper. Can someone help me how to caclulate this value propery.
This is the jsfiddle test link
You need to specify the transform origin; by default this is set to 50% and 0%, but you can override it with the transform-origin property like so:
#wrap {
-webkit-transform-origin:0 0;
-moz-transform-origin:0 0;
-ms-transform-origin:0 0;
transform-origin:0 0;
}
Shrink wrapping a div to some text is pretty straightforward. But if the text wraps to a second line (or more) due to a max-width (as an example) then the size of the DIV does not shrink to the newly wrapped text. It is still expanded to the break point (the max-width value in this case), causing a fair amount of margin on the right side of the DIV. This is problematic when wanting to center this DIV so that the wrapped text appears centered. It will not because the DIV does not shrink to multiple lines of text that wrap. One solution is to use justified text, but that isn't always practical and the results can be hideous with large gaps between words.
I understand there's no solution to shrink the DIV to wrapped text in pure CSS. So my question is, how would one achieve this with Javascript?
This jsfiddle illustrates it: jsfiddle. The two words just barely wrap due to the max-width, yet the DIV does not then shrink to the newly wrapped text, leaving a nasty right-hand margin. I'd like to eliminate this and have the DIV resize to the wrapped text presumably using Javascript (since I don't believe a solution exists in pure CSS).
.shrunken {text-align: left; display: inline-block; font-size: 24px; background-color: #ddd; max-width: 130px;}
<div class="shrunken">Shrink Shrink</div>
It's not the prettiest solution but it should do the trick. The logic is to count the length of each word and use that to work out what the longest line is that will fit before being forced to wrap; then apply that width to the div. Fiddle here: http://jsfiddle.net/uS6cf/50/
Sample html...
<div class="wrapper">
<div class="shrunken">testing testing</div>
</div>
<div class="wrapper">
<div class="shrunken fixed">testing testing</div>
</div>
<div class="wrapper">
<div class="shrunken">testing</div>
</div>
<div class="wrapper">
<div class="shrunken fixed">testing</div>
</div>
<div class="wrapper">
<div class="shrunken" >testing 123 testing </div>
</div>
<div class="wrapper">
<div class="shrunken fixed" >testing 123 testing </div>
</div>
And the javacript (relying on jQuery)
$.fn.fixWidth = function () {
$(this).each(function () {
var el = $(this);
// This function gets the length of some text
// by adding a span to the container then getting it's length.
var getLength = function (txt) {
var span = new $("<span />");
if (txt == ' ')
span.html(' ');
else
span.text(txt);
el.append(span);
var len = span.width();
span.remove();
return len;
};
var words = el.text().split(' ');
var lengthOfSpace = getLength(' ');
var lengthOfLine = 0;
var maxElementWidth = el.width();
var maxLineLengthSoFar = 0;
for (var i = 0; i < words.length; i++) {
// Duplicate spaces will create empty entries.
if (words[i] == '')
continue;
// Get the length of the current word
var curWord = getLength(words[i]);
// Determine if adding this word to the current line will make it break
if ((lengthOfLine + (i == 0 ? 0 : lengthOfSpace) + curWord) > maxElementWidth) {
// If it will, see if the line we've built is the longest so far
if (lengthOfLine > maxLineLengthSoFar) {
maxLineLengthSoFar = lengthOfLine;
lengthOfLine = 0;
}
}
else // No break yet, keep building the line
lengthOfLine += (i == 0 ? 0 : lengthOfSpace) + curWord;
}
// If there are no line breaks maxLineLengthSoFar will be 0 still.
// In this case we don't actually need to set the width as the container
// will already be as small as possible.
if (maxLineLengthSoFar != 0)
el.css({ width: maxLineLengthSoFar + "px" });
});
};
$(function () {
$(".fixed").fixWidth();
});
I little late, but I think this CSS code can be useful for other users with the same problem:
div {
width: -moz-min-content;
width: -webkit-min-content;
width: min-content;
}
const range = document.createRange();
const p = document.getElementById('good');
const text = p.childNodes[0];
range.setStartBefore(text);
range.setEndAfter(text);
const clientRect = range.getBoundingClientRect();
p.style.width = `${clientRect.width}px`;
p {
max-width: 250px;
padding: 10px;
background-color: #eee;
border: 1px solid #aaa;
}
#bad {
background-color: #fbb;
}
<p id="bad">This box has a max width but also_too_much_padding.</p>
<p id="good">This box has a max width and the_right_amount_of_padding.</p>
I guess this is what you are thinking about, it can be done in css:
div {
border: black solid thin;
max-width: 100px;
overflow: auto;
}
You can see it here: http://jsfiddle.net/5epS4/
Try this:
https://jsfiddle.net/9snc5gfx/1/
.shrunken {
width: min-content;
word-break: normal;
}
Lets say I have
CSS:
.mainWrap { position: relative; overflow: hidden; }
.wrap-boxes { positon: relative; }
.box { position:absolute (position and height is generated by plugin isotope: http://isotope.metafizzy.co/custom-layout-modes/centered-masonry.html }
HTML:
<div class"mainWrap">
<div class="wrap-boxes">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
</div>
clearfix applied to wrap-boxes won't work as it has elements with absolute position in it.
therefore i'd need to use jQuery to calculate the height of the boxes in order to extend wrap-box. I don't know the height of these boxes as they have random height and I do not know the total number of boxes as they are constantly generated by the client. I'd need a general jQuery that solves that. If i don't extend the mainWrap the boxes will be cut off and i need to use overflow: hidden for other reasons.
Any help on this?
Something like this maybe?
$.fn.wrapHeight = function() {
return this.each(function() {
var height = 0;
$(this).children().each(function() {
height += $(this).height();
}).end().height(height);
});
};
$('.wrap-boxes').wrapHeight();
Absolutely-positioned elements are no longer part of the layout. You need to use JavaScript to measure the size and position of the child elements and set the size of the parent element accordingly.
In pure JavaScript you could use the following:
var wrapbox = document.getElementById('mainWrap').childNodes[1],
els = wrapbox.childNodes,
i,
height = 0;
for (i in els) {
if(els[i].nodeType == 1) {
height += parseInt(els[i].offsetHeight);
}
wrapbox.style.height = height + 'px';
}
http://jsfiddle.net/AJLe7/1/
Notice I changed the class="mainWrap" to id="mainWrap" to simplify the answer...
I'm coding a slider and I have problems with the stylying of the container.
I have 3 div:
A div that sets the width and height of the slider
A container div with all the content divs (and the scroll for the slider)
Many divthat show different contents each
What I want to do is apply a negative margin on the second div to slide the content.
LIVE example: http://jsbin.com/efuyix/7/edit
JS:
function animate(element) {
var start = new Date();
var id = setInterval(function() {
var timePassed = new Date() - start;
var progress = timePassed / 600;
if (progress > 1) progress = 1;
element.style.marginLeft = -50 * Math.pow(progress, 5)+"px";
if (progress == 1) {
clearInterval(id);
}
}, 10);
}
CSS
.example_path {
overflow: hidden;
width: 50px;
height: 50px;
}
.example_block {
min-width: 100px;
height: 50px;
float:left;
}
.example_in_block {
width: 50px;
height: 50px;
float:left;
}
HTML
<div class="example_path">
<div class="example_block" onclick="animate(this)">
<div class="example_in_block" style="background-color:blue;"></div>
<div class="example_in_block" style="background-color:pink;"></div>
<div style="clear:both;"></div>
</div>
</div>
The problem:
The width of .example_block has to be exactly the same or more than (amount of content divs .example_block * 50 [width size of content div] ) to work.
For example, if I set the width size of the .example_block to 90, the pink div will be below the blue div and not beside it.
I want the container div to be dynamic so I don't have to set the specific width size.
How can I do this?
Simply remove the float:left in the .example_block.
See http://jsbin.com/efuyix/9/edit
Not with negative margins. You can probably set padding on one of the outer DIVs.
Also, min-width isn't going to be backwards compatible with older versions of IE.
Check this example: http://jsfiddle.net/5xBYN/6/
If the initial positioning is good, you can then use negative values on your container DIV (the third DIV) for top, left, right or bottom to achieve sliding.
Update:
Maybe this is closer to what you want. http://jsfiddle.net/5xBYN/7/
I'm still not sure what you are trying to do. Maybe edit the fiddle I posted and update your question with what I'm getting wrong if there is anything.