Positioning Divs semi-randomly, without overlaps - javascript

I have a page which has DIVs which contain short phrases (one-two words) of varying font sizes, which need to be positioned left-to-right according to a numerical parameter, and vertically so as not to overlap.
It's like a tag cloud, but there's information contained in the y-axis as well ("coolness" in this case - http://cool-wall.appspot.com)
How should I lay these out? I'm currently using a very messy series of DIVs like this:
<div style="position:absolute; top:150px;left:10px;right:10px;bottom:10px">
<!-- then, repeated, with different top, left and font-size values -->
<div style="align:center; margin:0; border:none; padding:0; float:left; visibility:visible; position:absolute; top:21%; left:56%; font-size:11px">
<div style="margin-top:0%; margin-right:50%; margin-bottom:0%; margin-left:-50%;">
<span style="display:inline"> ← </span>
<span style="display:inline"> Buzz </span>
<span style="display:inline"> → </span>
</div>
</div>
<!-- of course, followed by a close div -->
</div>
I use a stylesheet to extract some of those styles, and I realise that it's pretty poor CSS (and HTML)... but this was all I could hack together to do (almost) what I wanted to do. The main problem with the above (apart from it being confusing) is that I can't set the positioning so it doesn't overlap, because I don't know what size the font will be, nor how it will display onscreen.
Happy to use JavaScript to get it right. But I don't know where to start. Any tips?

There is a javascript property on the dom object that will tell you the height of the tag if you have the width set. I believe its called clientHeight
alert(document.getElementById('myElement').offsetHeight);
Try that (also see http://www.webdeveloper.com/forum/archive/index.php/t-121578.html)
OR
Try this
<span style="margin-top:${randomNumber}px;margin-bottom:${randomNumber}">randomtext</span>
<span style="margin-top:${randomNumber}px;margin-bottom:${randomNumber}">randomtext</span>
..
<span style="margin-top:${randomNumber}px;margin-bottom:${randomNumber}">randomtext</span>
Have all your element just display inline, output them in random order, and then set random margin's on them. This could all be done with server side code (or javascript if you want it client side).

The x-value is set on each one, you want to be as high on the page as possible (lowest y) as it can go without overlapping. Not too bad:
1) Render the container - position:relative; Render each item inside the container with "position:absolute; top:0; left:-1000; " - draw them all off screen.
2) One by one, move the element to it's needed x-coorinate and y=0; Check it with all previous render items to see if it collides, if it does, move it down one pixel until it doesn't collide:
var regions = [];
for(var i = 0; i < items.length; i++){
var item = items[i];
item.style.x = getX(item); // however you get this...
var region = YAHOO.util.Dom.getRegion(item);
var startingTop = region.top;
for(var iReg = 0; iReg < regions.length; iReg++){
var existingRegion = regions[iRegion];
while(region.intersect(existingRegion)){
region.top++;
region.bottom++;
}
item.style.y = (region.top - startingTop) + 'px';
}
}
It's important to just update the region and not actually move the dom node 1px at a time for performance reasons.
Put most important items first and they will render with more priority than items below them.

Don't position your elements absolutely. This is the reason they are falling on top of each other....

Related

Best ways to display width of multiple elements as text

Hi I'm looking at ways to specify the widths of a large number of objects on a page AND have each object's width displayed within it as text. The main aim is to avoid having a reference to the width anywhere (whether in the HTML, CSS or JS) more than once but I need potentially thousands of these objects on one page (currently I specify the width of the div and a text within it - too inefficient!).
So far I have this: https://jsfiddle.net/ghostfood/d6acdhq6/17/
<body onLoad="myFunction()">
<div id="object1" class="voteshare1" style="width:40.6%;">
This one is <span id="percentage1"></span></div>
<div id="object2" class="voteshare2" style="width:20.4%;">
This one is <span id="percentage2"></span></div>
<div id="object3" class="voteshare3" style="width:10.2%;">
This one is <span id="percentage3"></span></div>
<script>
function myFunction() {
var x1 = document.getElementById("object1").style.width;
var x2 = document.getElementById("object2").style.width;
var x3 = document.getElementById("object3").style.width;
document.getElementById("percentage1").innerHTML = x1;
document.getElementById("percentage2").innerHTML = x2;
document.getElementById("percentage3").innerHTML = x3;
}
</script>
</body>
The width must be a percentage but ideally would not include the percentage symbol in the displayed text (not sure how it's doing that as this is an example I found online then modified a bit - I do not know JS very well).
I've looked at D3 and amcharts for this briefly but I'm not sure they're best for handling hundreds of small stacked bar charts on one page and with lots of CSS control which is what I need. I may well be wrong!
Summary: Help me figure out a more efficient way of getting and displaying the (percentage) width (as set manually in HTML or JS and within a range of 10% to 100%) of an object within it as text (the caveat being that I need to do this for thousands of small objects on one page).
Set a common class to all divs that you want to get the width.
Select all of then with getElementsByClassName()
Loop through each one getting its width.
find the children span and add the string to it.
See below
function myFunction() {
var elements = document.getElementsByClassName("voteshare");
for (var i = 0; i < elements.length; i++){
var thisElement = elements[i];
var thisWidth = thisElement.style.width.toString();
thisElement.children[0].textContent += thisWidth;
}
}
<body onLoad="myFunction()">
<div id="object1" class="voteshare" style="width:40.6%;">
<span id="percentage1">This one is </span>
</div>
<div id="object2" class="voteshare" style="width:20.4%;">
<span id="percentage2">This one is </span>
</div>
<div id="object3" class="voteshare" style="width:10.2%;">
<span id="percentage3">This one is </span>
</div>
</body>
The problem with the JS is that you were referencing to object1 but the name of the <div> is object.
When a browser encounters an error, the execution of the script stops. That means that none of your code was running because the error was on the first line (of the function code).

How can I set the div to the height of two other divs with javascript while using bootstrap

I am using javascript to make images appear in a bootstrap template. They all have a with of 292.5px when on a full screen but I would like to make one of the columns set height to the height of two other columns put together. When looking for help I found this:
var right=document.getElementById('box').style.height;
var left=document.getElementById('slideshow3').style.height;
if(left>right)
{
document.getElementById('box').style.height=left;
}
else
{
document.getElementById('slideshow3').style.height=right;
}
But it wouldnt work for some reason. And I only need to change #box height to the height of #slideshow4 + #slideshow5
Thanks
---------------Update---------------
please see my jsfiddle Thanks
You almost got it right except that style.height properties are not straight numbers ie.. 10px or 100% so you cant compare the raw values, you must parseInt() them first or use .offsetHeight instead (or both) and then add the unit type back when setting them. Please note: .style properties ARE read/write. Also the .style.height property must be set in code before you can read it (so use offsetHeight instead) You are also missing the id attribute in your box div eg.. <div class="box" id="box" style="height: 185px;">
To fix your code above:
var rightHeight=parseInt(document.getElementById('box').offsetHeight);
var leftHeight=parseInt(document.getElementById('slideshow3').offsetHeight);
if(leftHeight>rightHeight){
document.getElementById('box').style.height=leftHeight+'px';
}else{
document.getElementById('slideshow3').style.height=rightHeight+'px';
}
The answer to your question:
var height1=parseInt(document.getElementById('slideshow4').offsetHeight);
var height2=parseInt(document.getElementById('slideshow5').offsetHeight);
document.getElementById('box').style.height=(height1+height2)+'px';
So, in your jsfiddle, box is a class, not an id, so it's .box and not #box.
If you want to set its height to be the sum of slideshow3 and slideshow4's height, use:
$('.box').height($('#slideshow3').height() + $('#slideshow4').height());
Try this function out
//sets the height of an element equal to the combined total height of the specificed slideshow id's
function setHeight(elementClass, slideshowIdArray){
var totalHeight = 0;
for(var i = 0; i < slideshowIdArray.length; i++){
totalHeight += parseInt($('#slideshow'+slideshowIdArray[i]).height());
}
$('.'+elementClass).height(totalHeight);
}
I forked your jsfiddle to provide a working example.
located here
I call this function once during intial page load and pass the box class to it along with an array of the two slideshows you want to match height.
I assume you want slideshow 6 to be flush along the bottom in relation to all other slideshows on the page, however, it seems that the H5 tag inside your box div has a margin set on it and this causes slideshow6 to be pushed slightly lower than the other slideshows. This can be adjusted using CSS (removing the margin), or the javascript can be altered to account for this. Your choice
Use jquery like this, It may help you
left = $("#left").height();
right = $("#right").height();
if(left<right)
$("#slideshow3").height(right);
else
$("#box").height(left);
I created a demo since you have not provided the HTML . Here is what I did
HTML
Please not the use of inline & block level elements
<span id="box">
<div id="cl1" class="c1"> Column 1</div>
<div id="cl2" class="c1"> Column 1</div>
</span>
<span id="slideshow3"> Column 3</span>
Javascript
var left = document.getElementById('box').offsetHeight
var right = document.getElementById('slideshow3').offsetHeight
if(left>right){
document.getElementById('slideshow3').setAttribute("style","height:"+left+"px");
}
else{
document.getElementById('box').setAttribute("style","height:"+right+"px");
}
CSS
span{
width:292.5px;
display:inline-block;
float:left;
}
.c1{
height:100px;
border:1px solid red;
}
#slideshow3{
border:1px solid green;
}
JSFIDDLE

Content After Slider Is Overwriting Slider. Why?

I am working on a slider that is working perfectly but the problem is that the content of web-page after the slider is overwriting on slider's 2-3 slides. I don't want to fix the height of slider but also want to show after slider content after every slides of slider. The code is shared below.
<script type="text/javascript">
window.addEventListener?window.addEventListener("load",so_init,false):window.attachEvent("onload",so_init);
var d=document, imgs = new Array(), zInterval = null, current=0, pause=false;
function so_init() {
if(!d.getElementById || !d.createElement)return;
imgs = d.getElementById("gallery").getElementsByTagName("li");
for(i=1;i<imgs.length;i++) imgs[i].xOpacity = 0;
imgs[0].style.display = "block";
imgs[0].xOpacity = .99;
setTimeout(so_xfade,1000);
}
function so_xfade() {
cOpacity = imgs[current].xOpacity;
nIndex = imgs[current+1]?current+1:0;
nOpacity = imgs[nIndex].xOpacity;
cOpacity-=.05;
nOpacity+=.05;
imgs[nIndex].style.display = "block";
imgs[current].xOpacity = cOpacity;
imgs[nIndex].xOpacity = nOpacity;
setOpacity(imgs[current]);
setOpacity(imgs[nIndex]);
if(cOpacity<=0) {
imgs[current].style.display = "none";
current = nIndex;
setTimeout(so_xfade,3000);
} else {
setTimeout(so_xfade,50);
}
function setOpacity(obj) {
if(obj.xOpacity>.99) {
obj.xOpacity = .99;
return;
}
obj.style.opacity = obj.xOpacity;
obj.style.MozOpacity = obj.xOpacity;
obj.style.filter = "alpha(opacity=" + (obj.xOpacity*100) + ")";
}
}
</script>
<style type="text/css">
#slider {max-height:700px;background:url(https://lh6.googleusercontent.com/-LLFEz-EyGbk/UyV9SbGPuhI/AAAAAAAAMgY/JNqf8X11dbk/s220/slider-loader.gif) #2e2e2e no-repeat 50% 50%;}
#gallery {padding:0;position:relative;margin:0 auto;max-width:1920px;}
#gallery li {list-style-type:none;width:100%;display:none;position:absolute;top:0;left:0;}
.gallery_img img {max-width:100%;}
.gallery_text {width:100%;margin:0 auto;text-align:center;position:absolute;top:-20%;left:0%;}
.gallery_text h2 {padding:0;line-height:70px;font-size:50px;font-weight:inherit;color:#fff;}
.gallery_text p {margin:20px 0;line-height:24px;font-size:20px;color:#ffee66;}
.gallery_text a {background:#77aa00;display:inline-block;padding:20px 70px;font-size:18px;font-weight:700;text-transform:uppercase;color:#fff;text-decoration:none;}
.gallery_text a:hover {background:#fff;color:#000;}
</style>
This Is The DIV Or Text Before The Slider.
<div class='clear'/>
<div id='slider'>
<ul id='gallery'>
<li style='position:relative!important;'>
<div class='gallery_img'><img alt='Google' src='https://lh4.googleusercontent.com/-Nh50j1-Bqws/UyV9Pv_wd3I/AAAAAAAAMf8/nsYUnwm35Gs/s1920/slide_1.jpg' title='Google'/></div>
<div class='gallery_text'><h2>Google</h2><p>Google is an American multinational corporation specializing in Internet-related services and products.</p><a href='http://www.google.com'>Open Google</a></div>
</li>
<li>
<div class='gallery_img'><img alt='Bing' src='https://lh4.googleusercontent.com/-eGrPYj9dz1c/UyV9QgDIh5I/AAAAAAAAMgM/mlcDdyufQJs/s1920/slide_2.jpg' title='Bing'/></div>
<div class='gallery_text'><h2>Bing</h2><p>Bing is a search engine that brings together the best of search and people in your social networks to help you spend less time searching and more time doing.</p><a href='http://www.bing.com'>Open Bing</a></div>
</li>
<li>
<div class='gallery_img'><img alt='Yahoo' src='https://lh6.googleusercontent.com/-L_s8vxgupPY/UyV9RKToZeI/AAAAAAAAMgQ/TWs-wy7lbrk/s1920/slide_3.jpg' title='Yahoo'/></div>
<div class='gallery_text'><h2>Yahoo</h2><p>Yahoo! Inc. is an American multinational Internet corporation headquartered in Sunnyvale, California.</p><a href='http://www.yahoo.com'>Open Yahoo</a></div>
</li>
</ul>
</div>
<div class='clear'/>
This Is The DIV Or Text After The Slider.
You can also see the live FIDDLE with error...
I think I've found the problem. You're trying to dynamically adjust the CSS display property of the <li> elements, alternating between block and none. But I don't think that's the right approach. All of the list item elements should be displayed and have display:block; at all times. The desired positioning can be achieved by setting the first <li> to position:static; (meaning it will be embedded in the graphical flow of the page), and all remaining <li> elements should be set to position:absolute; (meaning they will positionally "collapse" up to their parent container, thus causing them to sit snugly on top of the first <li>). To ensure only one <li> is visible at all times, it's enough to weaken the opacity of the others down to zero.
Thus, I made the following changes to your code:
Changed the first <li> from <li style='position:relative!important;'> to <li style="position:static;">.
Changed the display property of the #gallery li rule to block.
Commented out the 3 JS lines where you're messing with the .style.display property of <li> nodes.
Moved the definition of the setOpacity() function out of the so_xfade() function so that it will be stored at window.setOpacity and thus it will be accessible from all scopes (necessary for change #5).
Added the line for(i=0;i<imgs.length;i++) setOpacity(imgs[i]); to so_init() just after the line imgs[0].xOpacity = .99; to ensure that the opacities of all <li> elements are properly initialized.
I haven't used jsfiddle before, so I'm not sure if I did this right, but I clicked the "Update" button in the top menu bar and that dropped me into http://jsfiddle.net/yyathnom/2/, so I think you can use that to see my changes. Let me know if it works the way you want.
Edit: Sorry, I didn't realize each slide needed link functionality. For link functionality the current slide needs to be not just visible, but at the front of the stack. This can be accomplished with z-index.
A complication is that z-index does not apply to statically-positioned elements, but that can easily be overcome by changing the position:static property to position:relative for the first list item (which is actually what you had originally!). Relatively positioned elements are still in-flow, they can just be moved and have their z-index set.
Another complication is that z-index interacts strangely with opacity; the final "computed" opacity seems to take into account both the opacity CSS style and the z-index. So an image with 50% opacity on top of another image with 50% will result in a different appearance if you swap the z-indexes of the two images, even leaving both opacities at 50%. I originally tried to swap the z-indexes when the two images were at approximately equal (50%) opacities, but the visual jerk was undesirable, so I ended up just changing the z-index at the end, at opacity zero.
So, made the following additional changes:
Added two global conceptual constants, ZINDEX_UNDERNEATH (1) and ZINDEX_CURRENTSLIDE (100). Larger z-indexes mean "more in front" and smaller mean "further back" within the stacking context.
Added initialization imgs[i].style.zIndex = ZINDEX_UNDERNEATH; for all but the first <li>, and imgs[0].style.zIndex = ZINDEX_CURRENTSLIDE; for the first <li>.
Added the following two lines to set the new current slide to the visible z-index, just before current is reassigned:
imgs[current].style.zIndex = ZINDEX_UNDERNEATH;
imgs[nIndex].style.zIndex = ZINDEX_CURRENTSLIDE;
Result: http://jsfiddle.net/yyathnom/3/
Edit: To provide more detail on the original issue, it was caused by the fact that 2 of the 3 <li> elements had position:absolute, which means they were not in-flow, and although the first <li> had position:relative, meaning it was in-flow (but relatively movable), the code was regularly disabling it with display:none. Any element that is not in-flow will effectively have no "mass", meaning it won't push the following elements below it, but will instead let them "collapse up" to where it would have been positioned if it had been in-flow. Thus, when the first <li> was being set to display:none (which was when the other 2 <li>s were being displayed), there was no in-flow element there to keep the text following the slideshow images beneath the images, and so it collapsed up to the flow position of the images.

vertically center page around a form

I know vertical center in CSS is a pain to begin with, but I've just made it a bit more complicated. On my page, I have:
<ul id="complete">
</ul>
<form id="new_item_form">
<input type="text" id="add_item" placeholder="Type some tasks here"/>
</form>
<ul id="incomplete">
</ul>
It's for a basic task list. Tasks are added to the incomplete ul, and when completed move to the complete ul. What I want to do via css is have the text field vertically centered on the page and stay there, with the two lists butted up against it. I've been looking at all sorts of vertical alignment (a summary of forms found here: http://blog.themeforest.net/tutorials/vertical-centering-with-css/ ) but I can't seem to find a way that I can figure out how to adapt to allow what I need. How would I accomplish this style of fixed position centering?
EDIT:
Here's an image of what I'm looking for: https://www.dropbox.com/s/i0oit3v84j93b5g/Screen%20Shot%202012-10-11%20at%204.27.16%20PM.png
Is this what you want to obtain?
Of course, my code is a bit sketchy (use of height attribute on tds! Don't scold me to much). But you get the point.
If the height of the table is not known nor fix, but its parent height is known, it won't work (see this example) and you'll have to break it down.
If you just don't know any height at all, it's kind of hard to align...
Further reading on vertical-align
I can't think of any way to do this with CSS, but it's fairly easy to do with JavaScript/jQuery. Here is a working jsFiddle that does what you want on document load. You'd call the code again if you changed the lists, of course.
First, you enclose your lists and form in a div. I called this id="cmiddle". Then you use CSS to set the cmiddle div as position: relative. Then you use JavaScript code to get the enclosing window or frame height, calculate the center for the form, and then, subtract the upper list height to get the correct div position:
$(document).ready(function(e) {
// To work with frames, too
function getParentDocHeight($ele) {
for (;;) {
if (!$ele || !$ele.length) {
return $(window).height();
}
$ele = $ele.parent();
if ($ele.is("frame") || $ele.is("window")) {
return $ele.height();
}
}
}
var $cm = $("#cmiddle");
var formHeight = $("#new_item_form").outerHeight();
var viewHeight = getParentDocHeight($cm)
var formTop = (viewHeight - formHeight) / 2;
var divTop = formTop - $("#complete").outerHeight();
$cm.css("top", divTop);
});
Edit: Kraz was nice enough to add a simulation of adding list items to both lists and calling the code again to recalc. His jsFiddle here.
Well, I'm not sure what you are talking about
But generally,
put the line-height = the div's height. I will create a div around it if necessary
if some very particular situations, i do some math to manually center it
So if you want to centering 1 thing, create a div go around it with line-height = the div's height
And then make the div position: absolute, width, height, ....
Hope this helps

Grid of resizable elements with jQuery - are there any libraries that would support this?

I'm looking to create a 3x3 grid of elements, fixed inside of a larger parent element, that are all resizable. The catch is that I want all 9 child elements to always occupy exactly 100% of the space of the parent. So if I increase the width of the element in the top-left corner, then the two elements on the same row should decrease in size to accommodate this. For instance,
A B C
D E F
G H I
In the above grid, let's assume that each letter is 30px by 30px. If I drag-resize A and increase the width to 60px, then I would expect B and C to each decrease by 15 px.
Obviously this can be accomplished using native jQuery UI. Setting a containment parameter on each of the children in the resizable() initialization would take care of binding them all within the parent. The resize event that is fired whenever an element changes could also be examined to determine when each neighboring child might need to move. The thing is, this would be a fairly complex algorithm. It's doable, but it's a lot of work. Before I reinvent the wheel, I wanted to see if a library/plugin that would support this task exists, or anyone has knowledge of a similar open-source project?
Thanks.
Havent seen such a framework, but sound like an interesting idea.
Alot of things to consider though, like how should the other letters act accordingly?
Played a little:
$('.row div:odd').css('background','blue');
$('div').click(function(){
var orgWidth = $(this).width();
$(this).width(orgWidth+100);
var gridwith = $(this).parent().width();
var columns = 3;
var importantClass = '.'+$(this).attr('class');
var resizeTo = $(this).width();
var columnsWidth = (gridwith-resizeTo)/(columns-1);
$(this).parent().children().not(importantClass).width(columnsWidth);
return false;
});
And
<div class="row">
<div class="col1"></div>
<div class="col2"></div>
<div class="col3"></div>
</div>
<div class="row">
<div class="col4"></div>
<div class="col5"></div>
<div class="col6"></div>
</div>
<div class="row">
<div class="col7"></div>
<div class="col8"></div>
<div class="col9"></div>
</div>
And
.row {width:600px; overflow:auto; divst-style:none;}
.row div {float:left; width:200px; height:200px; background:red;}
.row div.odd {background:blue;}
How would that function into what ur consindering?

Categories