Write a wrapper object in Javascript

4.7k views Asked by At

First off, let me apologize if my question isn't worded correctly - I'm not a professional coder so my terminology might be weird. I hope my code isn't too embarrassing :(

I have a fade() method that fades an image in and out with a mouse rollover. I would like to use a wrapper object (I think this is the correct term), to hold the image element and a few required properties, but I don't know how to accomplish this. fade() is called from the HTML, and is designed to be dropped into a page without much additional setup (so that I can easily add new fading images to any HTML), just like this:

<div id="obj" onmouseover="fade('obj', 1);" onmouseout="fade('obj', 0);">

The fade(obj, flag) method starts a SetInterval that fades the image in, and when the pointer is moved away, the interval is cleared and a new SetInterval is created to fade the image out. In order to save the opacity state, I've added a few properties to the object: obj.opacity, obj.upTimer, and obj.dnTimer.

Everything works okay, but I don't like the idea of adding properties to HTML elements, because it might lead to a future situation where some other method overwrites those properties. Ideally, I think there should be a wrapper object involved, but I don't know how to accomplish this cleanly without adding code to create the object when the page loads. If anyone has any suggestions, I would greatly appreciate it!

Here's my fader method:

var DELTA = 0.05;

function fade(id, flag) {

  var element = document.getElementById(id);
  var setCmd = "newOpacity('" + id + "', " + flag + ")";

  if (!element.upTimer) {
    element.upTimer = "";
    element.dnTimer = "";
  }
  if (flag) {
    clearInterval(element.dnTimer);
    element.upTimer = window.setInterval(setCmd, 10);
  } else {
    clearInterval(element.upTimer);
    element.dnTimer = window.setInterval(setCmd, 10);
  }
}

function newOpacity(id, flag) {

  var element = document.getElementById(id);

  if (!element.opacity) {
    element.opacity = 0;
    element.modifier = DELTA;
  }

  if (flag) {
    clearInterval(element.dnTimer)
    element.opacity += element.modifier;
    element.modifier += DELTA;  // element.modifier increases to speed up fade
    if (element.opacity > 100) {
      element.opacity = 100;
      element.modifier = DELTA;
      return;
    }
    element.opacity = Math.ceil(element.opacity);
  } else {
    clearInterval(element.upTimer)
    element.opacity -= element.modifier;
    element.modifier += DELTA;  // element.modifier increases to speed up fade
    if (element.opacity < 0) {
      element.opacity = 0;
      element.modifier = DELTA;
      return;
    }
    element.opacity =
      Math.floor(element.opacity);
  }
  setStyle(id);
}

function setStyle(id) {

  var opacity = document.getElementById(id).opacity;

  with (document.getElementById(id)) {
    style.opacity = (opacity / 100);
    style.MozOpacity = (opacity / 100);
    style.KhtmlOpacity = (opacity / 100);
    style.filter = "alpha(opacity=" + opacity + ")";
  }
}
3

There are 3 answers

0
limc On

If you want to keep your HTML clean, you should consider using JQuery to set up the events.

Your HTML will look like this:-

<div id="obj">

Your JavaScript will look "something" like this:-

$(document).ready(function() {
    $("#obj").mouseover(function() {
        Page.fade(this, 1);
      }).mouseout(function(){
        Page.fade(this, 0);
      });
});

var Page = new function () {
    // private-scoped variable
    var DELTA = 0.05;

    // public-scoped function
    this.fade = function(divObj, flag) {
       ...
    };

    // private-scoped function
    var newOpacity = function (divObj, flag) {
       ...
    };

    // private-scoped function
    var setStyle = function (divObj) {
        ...
    };
};

I introduced some scoping concept in your Javascript to ensure you are not going to have function overriding problems.

2
Felix Kling On

You are right, adding the handlers in your HTML is not good. You also loose the possible to have several handlers for event attached to one object.

Unfortunately Microsoft goes its own way regarding attaching event handlers. But you should be able to write a small wrapper function to take care of that.

For the details, I suggest you read quirksmode.org - Advanced event registration models.

An example for W3C compatible browsers (which IE is not): Instead of adding your event handler in the HTML, get a reference to the element and call addEventListener:

var obj = document.getElementById('obj');

obj.addEventListener('mouseover', function(event) {
    fade(event.currentTarget, 1);
}, false);

obj.addEventListener('mouseout', function(event) {
    fade(event.currentTarget, 0);
}, false);

As you can see I'm passing directly a reference to the object, so in you fade method you already have a reference to the object.

You could wrap this in a function that accepts an ID (or reference) and every time you want to attach an event handler to a certain element, you can just pass the ID (or reference) to this function.

If you want to make your code reusable, I suggest to put everything into an object, like this:

var Fader = (function() {
   var DELTA = 0.05;
   function newOpacity() {}

   function setStyle() {}

   return {
       fade: function(...) {...},

       init: function(element) {
           var that = this;
           element.addEventListener('mouseover', function(event) {
               that.fade(event.currentTarget, 1);
           }, false);

           element.addEventListener('mouseout', function(event) {
               that.fade(event.currentTarget, 0);
           }, false);
       }
   };
}())

Using an object to hold your functions reduces pollution of the global namespace.

Then you could call it with:

Fader.init(document.getElementById('obj'));

Explanation of the above code:

We have an immediate function (function(){...}()) which means, the function gets defined and executed (()) in one go. This function returns an object (return {...};, {..} is the object literal notation) which has the properties init and fade. Both properties hold functions that have access to all the variables defined inside the immediate function (they are closures). That means they can access newOpacity and setStyle which are not accessible from the outside. The returned object is assigned to the Fader variable.

0
rforte On

This doesn't directly answer your question but you could use the jQuery library. It's simple, all you have to do is add a script tag at the top:

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js">

Then your div would look like:

<div id="obj" onmouseover="$('#obj').fadeIn()" onmouseout="$('#obj').fadeOut()">

jQuery will handle all the browser dependencies for you so you don't have to worry about things like differences between firefox and mozilla etc...