Expanding Vertical Space Issue with Floated Elements - html

I have a layout that requires a list of items to be organized into two vertical columns. The items are in a single list of <div>s, with the markup also being re-purposed for mobile, so I don't want to modify the structure.
The problem I have here is that each item has an expanding content area which the user can toggle, and when this content is expanded the vertical space for that column needs to expand downward with the other column staying fixed.
Right now I have a basic solution with floated items, but when I expand the content areas the vertical space expands in both columns instead of just the one.
Here's a link to an example of the functionality as I have it now, and below is a screenshot of what the desired behavior should be.
Is it possible to style this to support the required behavior? Or am I going to have to modify the structure of items in order to get this to work? Thanks in advance for your help!

Your premise is flawed. Document structure flows left-to-right, top-to-bottom. You will need to make some change to the structure... Easiest would be adding two containers for a left column and a right column. Otherwise, you're in for some tricky absolute positioning markup, and a little funky jquery, which I can only suggest with the addition of some unique IDs for each of the panels.
I would, personally, add ids such as panel1 through panel4 per your example, then use this javascript (or similar) as a jumping off point:
for(var i=1; i<=4; i++) {
$('#panel'+i).css('left', function(index) {
if(i%2 == 0) return "120px";
else return "0px";
});
}
$('.more').click(function(e) {
e.preventDefault();
$(this).parent().children('p').toggle();
var id = $(this).parent().attr("id");
switch( id ) {
case 'panel1':
console.log("panel1 found");
$('#panel3').css('top', function(index) {
var buffer = $('#'+id).height() + 20 + "px";
return buffer;
});
break;
case 'panel2':
$('#panel4').css('top', function(index) {
var buffer = $('#'+id).height() + 20 + "px";
return buffer;
});
break;
default: break;
}
});
With the default values in the css for those panels:
#panel1 { top:0px; }
#panel2 { top:0px; }
#panel3 { top:56px; }
#panel4 { top:56px; }
The less you tweak the html, the more work you'll create in javascript.
edit:
Proposed alternate Javascript to remove need to alter HTML, assuming two elements per row. Since we know the row to be the problem...
var ct = 1
$('#container > div').each(function(index, domEle) {
$(domEle).attr('id', 'panel'+ct);
$('#panel'+ct).css({
'position': 'absolute',
'left' : function(index, value) {
if(ct%2 == 0) return "120px";
else return "0px";
},
'top' : function(index, value) {
return (56 * Math.floor(ct/3)) + "px";
}
});
ct++;
});
$('.more').click(function(e) {
e.preventDefault();
$(this).parent().children('p').toggle();
var id = $(this).parent().attr("id");
switch( id ) {
case 'panel1':
$('#panel3').css('top', function(index) {
var buffer = $('#'+id).height() + 20 + "px";
return buffer;
});
break;
case 'panel2':
$('#panel4').css('top', function(index) {
var buffer = $('#'+id).height() + 20 + "px";
return buffer;
});
break;
default: break;
}
});
Now no changes need be made to the HTML, though you'll want to redo the click function to handle repositioning of elements after a click. I would make life easy and hide all .more items before expanding a new box, since it would mean having to calculate the heights of all elements above, but how much work you want to make is your business.

Here's a PURE CSS SOLUTION for four panels (I don't know if you intended to have more, and I do not have six [2 wide 3 high] working yet--and suspect it is not possible). It works in FF and IE8/9. See the fiddle: http://jsfiddle.net/bEgwB/203/
However, IE8 experiences a redraw bug that keeps panel 3 from moving so it needs extra javascript help (added in fiddle above), and IE7 needs some margin adjustments to get panel 4 positioned correctly (but does not have the redraw issue even without the extra javascript help). UPDATE (11-18-11): here's the fiddle for IE7 margin adjustment: http://jsfiddle.net/bEgwB/286/
EDIT: a previous version of my CSS had display calls that were unnecessary.
HTML
<div id="container">
<div class="panel one">
Panel 1<br />
<a class="more" href="#">more</a>
<p>More Info 1 with some additional content</p>
</div>
<div class="panel two">
Panel 2<br />
<a class="more" href="#">more</a>
<p>More Info 2 with some additional content</p>
</div>
<div class="panel three">
Panel 3<br />
<a class="more" href="#">more</a>
<p>More Info 3 with some additional content</p>
</div>
<div class="panel four">
Panel 4<br />
<a class="more" href="#">more</a>
<p>More Info 4 with some additional content</p>
</div>
</div>
CSS
a {color:yellow;}
#container {width:0 ; padding: 0 130px;}
.panel {
background-color:green;
padding:5px;
color:#fff;
width:100px;
}
.panel p {display:none;}
.panel.one {
float: left;
margin:10px 0 10px -120px;
}
.panel.two {
float: right;
margin: 10px -120px 20px 0;
}
.panel.three {
float: left;
clear: left;
margin:10px 10px 10px -120px;
}
.panel.four {
clear: right;
margin: 10px;
}
JAVASCRIPT
$('.more').click(function(e) {
e.preventDefault();
$(this).parent().children('p').toggle();
/* the following fix IE8 */
$(this).parent().parent().hide();
$(this).parent().parent().show();
});

You need to add two more div's - left and right column and split your items between these two divs. This is the way how to make them independent, here is jsfiddle for this.
HTML
<div id="container">
<div class="left">
<div class="panel">
Panel 1<br />
<a class="more" href="#">more</a>
<p>More Info 1 with some additional content</p>
</div>
<div class="panel alt">
Panel 2<br />
<a class="more" href="#">more</a>
<p>More Info 2 with some additional content</p>
</div>
</div>
<div class="right">
<div class="panel">
Panel 3<br />
<a class="more" href="#">more</a>
<p>More Info 3 with some additional content</p>
</div>
<div class="panel alt">
Panel 4<br />
<a class="more" href="#">more</a>
<p>More Info 4 with some additional content</p>
</div>
</div>
</div>
CSS
a {color:yellow;}
#container {width:300px; position:relative;}
.panel {background-color:green;padding:5px;color:#fff;width:100px;margin:10px;}
.panel p {display:none;}
.left {
width: 50%;
float: left;
}
.right {
width: 50%;
float: right;
}

May be you can do it column-count property like this:
a {color:yellow;}
#container {
width:300px;
-moz-column-count: 2;
-moz-column-gap: 50%;
-webkit-column-count: 2;
-webkit-column-gap: 50%;
column-count: 2;
column-gap: 50%;
}
.panel p {display:none;}
.panel {background-color:green;padding:5px;color:#fff;width:100px;margin:10px;}
.alt{
margin-bottom:90px;
}
Check this fiddle http://jsfiddle.net/bEgwB/87/
UPDATED
Check this:
http://jsfiddle.net/bEgwB/276/

There is a styling solution. To tie the two columns together they need an extra level of binding you can do that by adding a style attribute to "container", "display:table" and to the panels with "display:table-cell".
That will keep the heights in synch. Both "container" and the "panel" class must have a declared width or they really mess up the layout. IE is weak on support of these attributes, so that could be a problem with the solution.

If I correctly inderstand, that do you you want is something like this
http://jsfiddle.net/bEgwB/275/
You need to use both css properties display:inline-block and float:left to implement effect like on your jpg. If my markup right and this looks as expected, I may help you with javascript, if it not ok now
Good luck.

Related

CSS only - same width for multiple <span> elements, based on the largest

I'm curious if there is a way to set the width property for multiple <span> elements to the same value if they are not siblings to each other. The width should be based on the largest
span element.
The expected result would be something like a table where the left text will automatically grow.
So instead of this:
Text: Value
Larger Text: Value
I want it like this:
Text: Value
Larger Text: Value
I know this can be done with JavaScript (see example at the end), but I want to know if this is possible with CSS only.
And at best without changing the HTML structure, but I'm open for such an answer as well.
Some similar questions I've found, but they are about direct sibling spans. So it's not really fitting in my case:
Make adjacent sibling elements same width using only CSS
How can I make multiple spans equal width within a div
Why I want a CSS only solution and no JS?
Because I have to loop those elements twice:
To determine which of those elements is the largest and get the width from it
To set all elements the same width
Here is the working example with JS:
const variantNames = document.querySelectorAll('.variant-name');
let greatestWidth = 0;
variantNames.forEach( (name) =>
{
if(name.offsetWidth > greatestWidth)
{
greatestWidth = name.offsetWidth;
}
});
variantNames.forEach( (name) =>
{
name.style.width = greatestWidth + 'px';
});
.container,
.variant {
width: 100%;
}
.variant-name {
display: inline-block;
margin-right: 5px;
}
<div class="container">
<div class="variant">
<span class="variant-name">Size:</span>
<span>28</span>
</div>
<div class="variant">
<span class="variant-name">Waterproof:</span>
<span>Yes</span>
</div>
<div class="variant">
<span class="variant-name">Color:</span>
<span>Azure</span>
</div>
</div>
Easiest solution, that will work with the HTML structure you have - format the whole thing as a table.
.container {
display: table;
}
.container .variant {
display: table-row;
}
.container .variant span {
display: table-cell;
}
<div class="container">
<div class="variant">
<span class="variant-name">Size:</span>
<span>28</span>
</div>
<div class="variant">
<span class="variant-name">Waterproof:</span>
<span>Yes</span>
</div>
<div class="variant">
<span class="variant-name">Color:</span>
<span>Azure</span>
</div>
</div>

using imgix with html links for retina images

I am using imgix to server my images. They have a great library for serving jpegs at just the right size and pixel density. But it doesn't work when I need to add links to those images.
Here's the fiddle & the code:
jsfiddle.net/L95suygs/1/
<style>
...
.feature-img {
width:23%;
margin:0 1% .5em;
height:320px;
float:left;
overflow: hidden;
text-align: center;
overflow:hidden;
}
#media (max-width:1024px){
.feature-img {
width:48%;
margin:0 1% .5em;
}
}
#media (max-width:480px){
.header-img{
width:100%;
margin:0 0 .5em 0;
}
.feature-img {
width:100%;
margin:0 0 .5em;
height:200px;
}
}
</style>
<div class="container" id="example1">
<!-- Header Image -->
<div class="header-img">
<img class="imgix-fluid" data-src="//assets.imgix.net/examples/octopus.jpg?fit=crop&crop=faces" >
</div>
<div class="feature-img">
<img class="imgix-fluid" data-src="//assets.imgix.net/examples/jellyfish.jpg?fit=crop&crop=faces">
</div>
<div class="feature-img">
<img class="imgix-fluid" data-src="//assets.imgix.net/examples/lionfish.jpg?fit=crop&crop=faces">
</div>
<div class="feature-img">
<img class="imgix-fluid" data-src="//assets.imgix.net/examples/clownfish.jpg?fit=crop&crop=faces">
</div>
<div class="feature-img">
<img class="imgix-fluid" data-src="//assets.imgix.net/examples/fin.jpg?fit=crop&crop=faces">
</div>
</div>
<script type="text/javascript">
var options = {
updateOnResizeDown : true,
updateOnPinchZoom : true,
fitImgTagToContainerWidth: true,
fitImgTagToContainerHeight: true,
pixelStep : 10,
onChangeParamOverride: function(w, h) {
var dpr = Math.ceil(window.devicePixelRatio*10) /10;
return {"txt": "w:" + w + " h:" +h + " dpr:" + dpr,
"txtalign": "center,bottom",
"txtsize": 20,
"txtfont":"Helvetica%20Neue,bold",
"txtclr":"ffffffff",
"txtpad":20,
"txtfit":'max',
"exp":-2
}
}
};
imgix.onready(function() {
imgix.fluid(options);
});
</script>
The Short Answer
Add something like the following to your CSS:
.feature-img > a {
display: block;
width: 100%;
height: 100%;
}
This gives a definite size to your <a> tag, so its child image will be sized accordingly.
-- OR --
Change your HTML from this:
<div class="feature-img">
<img class="imgix-fluid" data-src="..." >
</div>
to this:
<a href="http://google.com" class="feature-img">
<img class="imgix-fluid" data-src="...">
</a>
This applies the .feature-img style that's already nicely defined to your <a> tag, rather than applying it to an unnecessary parent <div> and using the <a> tag as a child.
The Long Answer
Marking an image as imgix-fluid means it will always size itself to fit its container's width (and in this case height, since you're passing in fitImgTagToContainerHeight: true).
In your standard case (<img> tag wrapped in a <div>), this behaves exactly as expected. Your <div> tags size themselves properly thanks to your CSS, and imgix.js ensures that the images inside it are the proper size, because you've marked them as imgix-fluid.
However, when you wrap an image in an <a> tag as you've done with the second image in the example, the <img>'s parent container is no longer the handsomely-sized <div>, it's now an <a> with no styling applied to it whatsoever. And, because <a> is an inline element by default, it has no inherent sizing of its own--inline elements size themselves to fit their contents. The <a> sizes itself to fit the <img> inside of it (which has no src attribute, and therefore will be sized to something small but inconsistent from browser to browser), and imgix.js sizes the image inside it to be as small as its parent <a>. It's kind of a chicken-and-egg problem, but it ends in disappointment instead of continuing indefinitely.
As stated above, there are two solutions you could use:
Simply apply some styles to your <a> tag. If you set it to display:block; and set width and height to 100%, the anchor will automatically fill the space created by its parent <div> and consequently imgix.js will size the child <img> appropriately.
Ditch the parent <div> in this case and just make the <a> the container! Replacing the outer <div> with an <a> works perfectly, as long as you give the <a> the feature-img class.
For my money, the second approach seems cleaner and makes more sense.
Hope this helps!

resize target div based on clientwidth of other div whose width is set to auto

I'm trying to dynamically resize the width of one DIV (the target) based on the clientWidth of another (the source).
Note: The CSS Style of the source DIV is width:auto;
1st problem: It doesn't work... it must be my syntax or something..?
2nd problem: It needs to dynamically resize as user changes browser window (recall script on window resize). How ???
Javascript:
<script>
// START RESIZE WIDTH SCRIPT
function resizetarget(){
var resizesource = document.getElementById('section');
var sourcewidth = resizesource.style.clientWidth;
var resizetarget = document.getElementById('collpilewrapper');
var targetwidth = resizetarget.style.clientWidth;
targetwidth = sourcewidth;
}
</script>
CSS:
.section{
width:auto;
max-height:75%;
top:60px;
bottom:60px;
margin-bottom:60px;
left:280px;
right:0px;
position:absolute;
background-color:#FFF;
white-space:nowrap;
display:inline-block;
}
.section li{float:left; display: inline; }
.pane{
overflow:auto;
clear:left;
position:relative;
width:100%;
height:100%;
}
#collpilewrapper{
min-width:900px;
min-height:100%;
float:left;
background-color:#69C;
display:table-column;
}
HTML:
<!--start content-->
<div class="section">
<div id="pane-target" class="pane">
<ul class="elements" style="width:16000px">
<!--box 0 -->
<li>
<!-- START landing group -->
<div id="collpilewrapper">
<div id="collpile1">
<img src="images/coll-missy-714x737.jpg" /></div>
<div id="collpile2">
<img src="images/coll-girls-602x476.jpg" /></div>
<div id="collpile3">
<img src="images/coll-toddler-421x447.jpg" /></div>
</div>
<!-- END landing group -->
</li>
<!--box 1 -->
<li>
<!-- START missy group -->
<div id="adbox">
<img class="largead" src="images/storypic-na.jpg" />
</div>
<div id="adbox">
<img class="largead" src="images/videostill1.jpg" />
</div>
<!-- END missy group -->
</li>
</ul>
</div>
</div>
<!--end content-->
jsfiddle
I used jQuery since I find it easier to work with. There's a couple of problems I noticed. You were targeting the element by ID but section is a class, not an id. style.clientWidth isn't officially supported from what I saw when I googled it. It looks like it's something that IE used to use (honestly I don't know anything about it).
jQuery
function resizeWidth(source, target){
var sourcewidth = $(source).width();
$(target).css({
"width": sourcewidth
});
}
$(window).resize(function() {
resizeWidth("#section", "#collpilewrapper");
});
Whatever code you place inside of this will run when the window has been resized.
$(window).resize(function() {
});
resizeWidth() is a function that I made myself. It takes in a source and a target.
width() will give you the width of an element.
css() allows us to directly change the CSS.
Lastly, you'll notice that it doesn't look like the large blue box is re-sizing. That's because you've put a minimum width of 900px. Also, the event will only fire when you re-size the browser. It won't do anything when you first load up a page.

Even out space layout (table)

For a web application I'm creating (in Umbraco, but don't think that really matters in this case) I need a page that can show an overview of different media types; audio, video and images.
No problem there, for images and videos (hosted on YouTube) I will show a thumbnail and for audio I will show a static image.
The rough layout of an item will be that the image is shown on top, and below that is some info like the title and a short description.
Now because of the difference in dimensions of the images (thumbnails can have a variable size, the audio static image will probably always be smaller than the thumbnails, etc.) one item (or column if you will) can be of less width than another.
What I would like to do is show three items per row, and when the row isn't completely filled I would like to fill it up with a colored box. But that box should not always be at the end, it could also be in between, or the beginning. It just is inserted 'randomly' when a space fill is needed.
Because a picture says more than 1000 words (wire-frame of what I'm trying to describe);
Now my question; is this at all possible? If yes, how?
I can't wrap my mind around it, it can't be done in pure HTML and CSS I think. Because you couldn't determine how big an item is and if a 'filler' is needed.
The rough HTML I have right now is something like this:
<table id="portfolio">
<tr>
<td>
<div class="portfolioItem">
<div class="portfolioItemImage">
<a rel="prettyPhoto" href="http://www.youtube.com/watch?v={video}"><img src="http://img.youtube.com/vi/{video}/1.jpg"/></a>
</div>
<br clear="both" />
<div class="portfolioItemDescription">
<h3>Title</h3>
<p>Description lorem ipsum etc.</p>
</div>
</div>
</td>
</tr>
</table>
Of course there is some more dynamic stuff in there to determine whether it is a video, audio or image, determine when to start a new row, etc. but that isn't relevant here.
Here is the CSS associated with it:
#portfolio {
width:100%;
}
#portfolio td {
width:33%;
}
#portfolio .portfolioItem {
border: 1px solid #000;
}
#portfolio .portfolioItem .portfolioItemImage {
margin:0px;
padding:0px;
}
Again; can this be done? And how?
Thank you!
I think that what you want is jQuery Masonry or the Wookmark jQuery Plugin.
I would create the grid using DIVs instead of TABLES, regardless I think this is what you are looking for?:
#portfolio td
{
min-width:33%;
}
EDIT:
Here is a rudimentary example of a grid created with DIV's:
http://jsfiddle.net/rdtnU/
<div class="con">
<div class="row">
<div class="cell">a</div>
<div class="cell">b</div>
<div class="cell is_last">c</div>
</div>
<div class="row">
<div class="cell">d</div>
<div class="cell">e</div>
<div class="cell is_last">f</div>
</div>
</div>
.con {}
.row { width:340px; margin:0 0 20px 0; overflow:hidden; }
.cell { width:100px; margin:0 20px 0 0; float:left; background:orange; }
.is_last { margin:0; }​
I would use the div's as suggested but I would not limit myself to the row/columns as stated. I would use a more fluid layout even if it is for a specified width of a certain section.
The following will only work if you know the width of the div with the content, to allow the floating to occur (this could work if there is a min-width or if your code can determine the size of the image)
Here is the HTML
<div class="elements">
<div class="singleElement">
text and graphics here.
</div>
<div class="singleElement">
text and graphics here.
</div>
<div class="singleElement">
text and graphics here.
</div>
<div class="singleElement">
thisonewillpushthewidthoftheboxfartherthanthe150pxwidth
</div>
<div class="singleElement">
small text
</div>
</div>
Here is the CSS (I put some simple background colors so you can see what is going on with the width and how things are tucked in where space is available.
.elements { overflow: hidden; width: 500px; background: #FCC; }
.singleElement { padding: 5px; white-space:nowrap; float: left;
height: 200px; min-width: 100px; background: #CCC;margin: 0 10px 10px 0; }
Please note the details of the styles are just for demonstrating the example. They can be altered to fit your need.
EXAMPLE: Here is the example in jsFiddle.

how to select plain HTML-text in CSS?

Does anyone of you know how to select plain HTML-text in CSS?
I have the following structure:
<div id="A">
<p class="caption"> caption1 </p>
<div class="tabs">
<div class='moving_bg'></div>
<p class="text tab_item"> content</p>
<p class="text tab_item"> content</p>
<p class="text tab_item"> content</p>
<p class="text tab_item"> content</p>
<p class="text tab_item"> content</p>
<p class="text tab_item"> content</p>
caption2
<p class="standardtext tab_item">content</p>
<p class="standardtext tab_item"> content</p>
<p class="standardtext tab_item"> content</p>
</div>
How do I select the caption2 in CSS?
As soon as I assign a class or a p-tag to it, it breakes the structure of the tab-plugin I'm using.
Edit:
I used this plugin and changed it a little bit into:
var TabbedContent = {
init: function() {
$(".tab_item").mouseover(function() {
var background = $(this).parent().find(".moving_bg");
$(background).stop().animate({
top: $(this).position()['top']
}, {
duration: 300
});
TabbedContent.slideContent($(this));
});
},
slideContent: function(obj) {
var margin = $(obj).parent().parent().parent().parent().find(".slide_content").width();
margin = margin * ($(obj).prevAll().size() - 1);
margin = margin * -1;
$(obj).parent().parent().parent().parent().find(".tabslider").stop().animate({
marginLeft: margin + "px"
}, {
duration: 300
});
}
}
$(document).ready(function() {
TabbedContent.init();
});
You can't. You can only select elements and pseudo-elements/classes.
You might be able to get away with styling .tabs and then overriding the styles on .tabs > *.
As soon as I assign a class or a p-tag to it, it breakes the structure of the tab-plugin I'm using.
Edit the plugin then.
You can use the * selector at the top of your CSS. It will initially style all elements on your page, even plain text, and then apply the rules for the rest of your selectors.
NOTE: This selector is known to be slow, and you will have to override any problematic rules in it. (Check CSS Lint)
* {
font-size: 13px;
color:red
}
(...)
.tabs { foo:bar; jim:jam }
.tabs > * { foo:original; jim:original } /* Every child element of tabs */
This requires you to know what the inherited/original values are, likely re-specifying styles already declared, but it will work.
If this is a prohibitive number of styles, you could modify your rules like:
#a, #a .tabs > * { ...many styles applying generally ... }
.tabs { ...specific styles for just this element... }
The specificity rules of CSS will cause the rules you specify specifically for the child elements to take precedence over those specified on the parent.
You need to select it as part of <div class="tabs"> so
div.tabs{
/* whatever */
}
as it exists in that div.
You may need overwrite some styles in the p.tab_item