1

I would say that I'm great with HTML and CSS, it's when I start to touch JavaScript that I struggle; I can understand some of it but I couldn't write it for my life! I'm trying to learn though, any ways; I've put together this jQuery script with the goal to vertically center a div between a relative positioned top div, and an absolute positioned bottom div. It's not working out for me, lol.

Because I'm trying to learn how to work with jQuery and create my own scripts, I'd like to try and get this working. However, if there is a better way to accomplish my goal, I would greatly appreciate your input in that way as well!

<script type="text/javascript">
$(document).ready(function() {
$(window).ready(function(){
vertical_height()
});
$(window).resize(function(){
vertical_height()
});
function vertical_height(){
var doc_height = $(document).height();
var head_height = $(".headcontent").height();
var mid_height = $(".midcontent").height();
var foot_height = $(".footer").height();
var pos_height = Math.round(head_height+foot_height);
var neg_height = Math.round((head_height-foot_height)/2);
var fin_height = Math.round(doc_height-(pos_height-neg_height));
$('.midcontent').css({
"marginTop","-"+fin_height+"px",
"marginBottom","-"+fin_height+"px"
}
}
});
</script>

.headcontent is the top div.

.midcontent is the middle div that I'd like to center.

.footer is the bottom div.

mattroberts33
  • 240
  • 4
  • 18
  • Does it work already? If so, move to http://codereview.stackexchange.com/, if not please describe the errors. – Bergi Aug 03 '12 at 00:15
  • Btw: You can add the function directly as an event listener: `$(document).ready(vertical_height); $(window).resize(vertical_height);`. Also, there is no `$(window).ready` - remove that – Bergi Aug 03 '12 at 00:16
  • Thanks, here's the idea so you can get an idea of what I'm trying to do! http://jsfiddle.net/mattroberts33/nBUnt/1/ – mattroberts33 Aug 03 '12 at 00:38

2 Answers2

1

Just a little tip when calculating something, never round until you get to your final number.

h = Header Height

f = Footer Height

m = Middle Height

d = Document Height

s = Height of the space above or below the div

Here we assume the height of the document is equal to that of all the other elements combined. Since there is a space above and below the middle div that is equal we have two spaces.

d = h + f + m + 2s

d - h - m -f = 2s

(d - h - m - f) / 2 = s

Let's put this into some javascript

$(document).ready(function() {

    $(window).resize(vertical_height());

function vertical_height() {
    var doc_height = $(document).height();
    var head_height = $(".headcontent").height();
    var mid_height = $(".midcontent").height();
    var foot_height = $(".footer").height();
    var fin_height = Math.round((doc_height - head_height - mid_height - foot_height) / 2);
    $('.midcontent').css("margin-top", fin_height)
  }
});

For the css we only need to add a top margin as this will push it away from the header because I am assuming the absolutely positioned footer will be stuck to the bottom.

Here is a working fiddle: http://jsfiddle.net/nBUnt/14/

Jrod
  • 11,822
  • 1
  • 32
  • 38
  • You are completely correct as to what I'm doing! I just don't understand how to get it to work... Here: http://jsfiddle.net/mattroberts33/nBUnt/4/ – mattroberts33 Aug 03 '12 at 00:40
  • Ah! I used your equation, so it works! Well, for the most part... http://jsfiddle.net/nBUnt/14/ I've got this far lol, now how do I make it so that the middle div doesn't overlap with the top one, but stays right under it when there isn't enough room to be centered? – mattroberts33 Aug 03 '12 at 18:12
  • If you add a check to make sure fin_height is not negative it should work. I will update. http://jsfiddle.net/nBUnt/19/ The other issue you will have to tackle is that the footer since it is absolutely positioned will go above both the midcontent and headcontent. I would recommend implementing a stick footer technique such as http://ryanfait.com/sticky-footer/ – Jrod Aug 03 '12 at 18:42
  • I like the idea of a sticky footer, I'll try it out. – mattroberts33 Aug 03 '12 at 23:07
1

Here's your code, tidied into something that will run :

$(function() {
    function vertical_height() {
        var doc_height = $(document).height();
        var head_height = $(".headcontent").height();
        //var mid_height = $(".midcontent").height();//not used
        var foot_height = $(".footer").height();
        var pos_height = head_height + foot_height;
        var neg_height = (head_height - foot_height) / 2;
        var fin_height = doc_height - (pos_height - neg_height);
        $('.midcontent').css({
            "marginTop": "-" + fin_height + "px",
            "marginBottom": "-" + fin_height + "px"
        });
    }
    $(window).resize(vertical_height).trigger('resize');
});

Now you can concentrate on getting the algorithm right.

As it stands, combining and simplifying expressions, I get :

var fin_height = doc_height - head_height*3/2 - foot_height*3/2;

which doesn't look right to me but I could be wrong.

EDIT

To avoid overlap, try this version :

var $$ = {}; //jQuery object cache.
$$.w = $(window);
$$.h = $(".headcontent");
$$.m = $(".midcontent");
$$.f = $(".footer");
function vertical_height() {
    var w = $$.w.height(),
        h = $$.h.outerHeight(true),
        m = $$.m.outerHeight(),
        f = $$.f.outerHeight(true),
        fin_height = Math.max(0, (w - h - m - f) / 2);
    $$.m.css('marginTop', Math.floor(fin_height)).css('marginBottom', Math.ceil(fin_height));
    $$.h.text(fin_height);
};
$(window).on('resize', vertical_height).trigger('resize');

DEMO

Notes

  • position:absolute removed from the footer's CSS
  • jQuery objects now cached in $$ to give the resize handler less work to do.
  • Calculation now based on window height not document height.
  • The three divs now measured with .outerHeight(false) to include vertical padding and borders.
  • Overlap is prevented by Math.max(0, ...), which ensures fin-height can't go negative.
  • The statement $$.h.text(fin_height); is for debugging and can be removed.

EDIT 2

The following code will "debounce" the resize event.

Replace :

$(window).on('resize', vertical_height).trigger('resize');

with :

var do_resize;
$(window).on('resize', function(){
  clearTimeout(do_resize);
  do_resize = setTimeout(vertical_height, 100);
}).trigger('resize');
Beetroot-Beetroot
  • 17,664
  • 3
  • 35
  • 44
  • In theory, 1) I'm taking the header and the footer out of the document height, 2) then taking the difference between the header and the footer and dividing that by two, 3) and finally I'm taking out what I got in number "2" from the header and footer height together... – mattroberts33 Aug 03 '12 at 00:58
  • I'm not 100% sure how to go about this, but the goal is that there's two div's the bottom one is absolute positioned, and I want the middle div to be vertically centered. I've figured it out before but it was using z-index with a lot of layers, I want to make it so that if I were to shrink the page, the div's wouldn't be over lapping - now it's just the bottom div that overlaps but that's because it's absolute position. – mattroberts33 Aug 03 '12 at 00:58
  • http://jsfiddle.net/nBUnt/14/ I've got this far lol, now how do I make it so that the middle div doesn't overlap with the top one, but stays right under it when there isn't enough room to be centered? – mattroberts33 Aug 03 '12 at 18:11
  • http://jsfiddle.net/nBUnt/14/ The space between the top and the middle is much less than that of the middle and the bottom, and it seems that with each pixel it moves, it vibrates in a sense as it goes up or down... We are getting closer though, the space needs to be the same though, how would we go about doing this? – mattroberts33 Aug 03 '12 at 21:09
  • You, my friend, are amazing! I haven't seen this anywhere and it seems like something people would think about but wouldn't try to do... Hopefully this helps someone else too! – mattroberts33 Aug 03 '12 at 23:58
  • Matt, you got a name! It's been a pleasure. – Beetroot-Beetroot Aug 04 '12 at 00:50
  • Alright, there's just one more problem to sort out. The bottom div appears to move up and down, when you resize the window, per pixel. – mattroberts33 Aug 04 '12 at 00:52
  • Matt, that pixel jog thing is caused by rounding up odd numbers divided by 2. I have revised the code and the DEMO. It now rounds up the top margin and rounds down the bottom. Thus, half-evens (integers) are unaffected and half-odds share the error between top and bottom. – Beetroot-Beetroot Aug 04 '12 at 02:19
  • The effect is very smooth in IE and slightly jerky in Opera. I'm afraid I can't do any more to make it smoother. – Beetroot-Beetroot Aug 04 '12 at 02:31
  • jsfiddle.net/mattroberts33/GTttK/5 I've added "t = Math.max(0, (w - h - m - f - 2) / 2); fin_height = Math.round(t / 1) * 1;" to get rid of the 0.5, although the glitch is still there - just barely - so I've also raised the bottom div by two pixels. I guess the question now is: Is there any way to hide the over flow and only make it auto when the bottom div is -20 on the bottom of the window? That would be a good fix, seeing as people shouldn't theoretically be resizing the window rapidly enough to notice the glitch any ways! Thanks again! – mattroberts33 Aug 04 '12 at 08:05
  • I know this isn't a working phrase, but could you translate it to something that works? I just want to take the height of the three divs, and if the height of the window is smaller, the css overflow will be auto... "if (h + m + f) > window height, then .css("overflow", "auto"); ... else .css("overflow", "hidden");" – mattroberts33 Aug 04 '12 at 08:06
  • Matt, by reintroducing `Math.round()` you have put the code back to where it was one iteration ago. I got rid of it to prevent overscanning the window height when `(w - h - m - f)` is odd. For sure you can subtract 2 to mitigate the effect but it's not a neat a solution as splitting the half-odd error with separate `Math.ceil()` for the top margin and `Math.floor()` for the bottom. – Beetroot-Beetroot Aug 04 '12 at 10:41
  • By all means try conditional overflow but I expect it will be unsuccessful. In some browsers the javascript simply isn't fast enough to respond before the scroll action has occurred. You will see that the unwanted scroll bars only appear when the window height is reducing. With an expanding window they don't appear. This means the algorithm is sound and it must be a speed of response issue. – Beetroot-Beetroot Aug 04 '12 at 10:44
  • I'm sorry my friend, I forgot to give you the green check mark! I was just looking back at this answer, you've done an amazing job, @Beetroot-Beetroot. It works great and the pixel jog thing doesn't make too much of a difference, to fix that I would just leave a few pixels of space below the bottom div. Looking back at this though, if we could give it a few milliseconds of delay time before the middle box moves between the top and bottom boxes, [in theory] that would completely remove the pixel jog effect... – mattroberts33 Jan 15 '13 at 08:00
  • Hi Matt, thanks for the green tick. I'm not sure but "debouncing" the resize event may give a better effect. See Edit 2 above. – Beetroot-Beetroot Jan 15 '13 at 20:55
  • Thanks for the quick reply, yeah I was looking at setTimeout. I agree though that it may not have produced the effect I had in my mind but that's probably the closest we'll get haha - I would probably be hiding any overflow - but I'll play around with that, thanks again, @Beetroot-Beetroot =) – mattroberts33 Jan 15 '13 at 21:32