A Blog about Thijs de Vries

A blog

Scrolling Element

So they other day I was thought to mysel: “why don’t I add one of those annoying scrolling advertising thingies at the side of my page so that all my users are forced to see adds wherever they are on my page?” Than I realized, those things are really annoying. It’s not so much that they follow you wherever they scroll, it’s more that they don’t scroll really smoothly (or at least that’s why I hate them). Most of these scrolling ads use javascript to observe when you scroll and change the position. Why not just use the css position attribute and set it to fixed. Fixed makes the div position relative to the browser window, and not any other elements. I added the following css.

1
2
3
4
5
#ads{
   position: fixed;
   top: 10px;
   left: 1100px;
}

This worked out really well, the ads were right where they should be. When I scrolled, the element moved with the window smoothly instead jittering about while scrolling. Everything was great … until I resized the window. When I made the window smaller, the ad div would move away from the content div. When I made the window bigger, the ad div would overlap the content div (though that might increase ad revenue by making people click on it by accident. What I really wanted was for the top attribute to be fixed to the browser window and the left attribute absolute to the content. Unfortunately css only has the position property which effects both top and bottom (if I’m wrong, please let me know).

After a bit of thinking, I decided, instead of observing when people scroll, why not observe when people resize their windows? You would think resizing the window happens a lot less often. The code below was written using the prototype 1.6.0.3. Got to prototype to download the latest version.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var ScrollingElm = Class.create({

  initialize: function(target_div, parent_div, options){
    this.target = $(target_div);
    this.parent = $(parent_div);
    this.handleOptions(options);
    this.setStyle();
  },

  handleOptions: function(options){
    this.options = Object.extend({
      top: 20,
      left: 800
    }, options || {});
  },

  setStyle: function(){
    leftOffset = this.parent.positionedOffset()[0] + parseInt(this.options.left);
    leftPos = leftOffset + "px";
    topPos = this.options.top + "px";
    this.target.setStyle({
      position: 'fixed',
      left: leftPos,
      top: topPos
    });
    this.target.show()
  },
});

Essentially I pass a target element, which is the element we want to adjust the position to, and the parent element which we want to use as the anchor for the left attribute. The default values I set to top 20 and left 800, I figured they would work well for many website. These can be overridden by passing the appropriate options. The top attribute is relative to the browser since the position is set to fixed. Technically left is also relative to the browser window, but the offset is dynamically calculated with prototypes Element.positionedOffset method. You may think that I would be able to use Prototypes Event.observe to detect when the window is resized. Unfortunately, the resize event seems to not be supported by prototype. I got around this by creating a ScrollingElm object and than manually setting the onresize attribute in the body tag.

1
var scroller = new ScrollingElm('ads', 'container', {top: '10', left: '750'});
1
2
3
4
5
6
<body onresize="scroller.setStyle()">
    <div id='ads' style='display: none;'>
         add content
    </div>
   ... content
</body>

Unfortunately this relies on creating a global variable named scroller but it gets the job done. I may explore further why Prototype does not support the resize event but this works for now. I set display to none because the javascript will show the element and this way the ad div will not mess up the layout before the js loads. I’ve tested this script in Firefox 3.5.6, Chrome 4.0.249.43, and Internet Explorer 7. Let me know if you find a browser it does not work with and I may look into it.

Comments