views:

426

answers:

3

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%;"> 
      <a href="foo"><span style="display:inline"> &larr; </span></a> 
      <a href="bar"><span style="display:inline"> Buzz </span></a> 
      <span style="display:inline"> &rarr; </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?

+1  A: 

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).

Zoidberg
Is it possible to do this automatically, without doing a whole lot of calculations in JavaScript? From what I can tell, with this property, I'd need to have a function for calculating the parts of the screen which are taken up with DIVs, and either keep track of this or recalculate each time... right?
Richard
Not with position absolute. The only way I can see to do what you want to do is with Javascript. Floating elements will cause them to float to the left (or right) of the screen and take up as much horizontal space as possible. However this doesn't work for vertical space.The javascript to do such a thing would not be too difficult. You just have to loop over the elements participating in this and using the width and height, calculate the best place to put them.
Zoidberg
OK, interesting. Also, you mentioned that this works "if you have the width set". I don't know how big it will be until it's rendered, as it depends on the font rendering and font size. I would think that I would also need a similar function to get the width, so I can make the screen usage calculation. I presume I can use offsetWidth in the same manner?
Richard
yes, offset width does the same. The link I posted with it shows the person also using scrollHeight and scrollWidth, (he gets the max between the scrollHeight and offsetHeight). That should work for you. It worked for me when I was developing an authoring tool that required dynamic changes of the css. The css could change teh size of all drag and drop elements, and as a result required a reposition of some stuff.
Zoidberg
Thanks - that's a really good answer. I'm still hoping someone will know of a way to make the browser's layout engine do this automatically. :-)
Richard
Now that I think about it... you could NOT position them absolutely, and randomize the order in which they are rendered in the html, and then set random Margins on them. So set a marin top and margin bottom on them and see what happens, that may work.
Zoidberg
I will put my last comment in an answer
Zoidberg
That sounds like it would give pretty much random distribution. I want to control the y-value, as the position left-right represents something in this app.
Richard
sorry, control the x-value.
Richard
then you could just do margin-top and the inline display will ensure that elements are ordered left to right based on the order they are output. A random margin top will control the Y value on the page. if you don't want display inline, then display block with float:left will work too.
Zoidberg
For the x-value, margin-left.
Zoidberg
A: 

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

Jason
OK, but how should I position them? I want an absolute X value, and any Y value which doesn't cause an overlap.
Richard
A: 

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.

Steve Brewer
This looks really good. But I think there are a few bugs in the above code: "... regions[iRegion];" should be "regions[iReg]"; And you need to add the regions to the array at the bottom of the outermost for loop: regions.push(region); ... Also, it requires Yahoo's YUI code, which I think is best instantiated by: <script src="yui.yahooapis.com/2.7.0/build/…; <script src="yui.yahooapis.com/2.7.0/build/…; Does that sound about right to you? YUI seems pretty interesting.(sorry about formatting of this comment)
Richard