Javascript, Loading/Processing notice display.

Javascript keywords, functions and properties used :

function, var, new, this, delete, Array(), push(), splice(), if, else, length, ++, setTimeout(), parentNode, style, offsetTop, offsetWidth, offsetHeight, top, left, visibility, createElement(), getElementsByTagName(), appendChild(), removeChild(), className,

System initialization...

Initializing the manager/factory on script file loading

So that processes needing notification displays needn't worry about whether or not the manager/factory is actually instantiated or not, an instance of the manager/factory class is instantiated when the script containing the notification display system is loaded.

var notify = new notifier();

This statement simply creates an instance of the notifier class and is best, although not 100% necessarily placed at the end of the script file in which the major compnents of this system, the notifier class and the notifyWindow class are declared.

Instantiating a given notice is then accomplished simply by executing a statement similar to notify.display("some_ID, "Some notice to display");

Notice manager/factory...

Notice manager/factory

The following code section is instantiated as an object by the previous shown statement when the script is loaded.

The instantiated object is then used to both cause the creation and destruction of notices as required.

function notifier () {
  var myWindows = new Array();
  this.display = display;
  this.kill = kill;
  
  function display (id,text) {
    var wind = new notifyWindow();
    var thisWind = new Array();
    thisWind['id'] = id;
    thisWind['ptr'] = wind;
    wind.open(text);
    if (myWindows.length > 0) {
      wind.setPrev(myWindows[myWindows.length - 1]['ptr']);
    } else {
      wind.setTop();
    }
    myWindows.push(thisWind);
  }
  
  function kill (id) {
    for (var x=0; x < myWindows.length; x++) {
      if (myWindows[x]['id'] == id) {
        myWindows[x]['ptr'].close();
        delete myWindows[x]['ptr'];
        myWindows.splice(x,1);
      }
    }
  }
}

The notifier class object is initialized with an array to hold ID/pointer pairs for created notification agents as well as pointers to the two class methods it implements, display() and kill()

The display() class method takes an ID and text to be displayed in the notification agent as input and then performs the following steps :

  1. Create a new instance of notifyWindow. Make sure to NOT create the new agent using the var Javascript keyword or it will not be able to be deleted.
  2. Create a new array element to contain the newly created notification agent's ID and object pointer.
  3. Call the new notification agent object's open() method passing it the text to be displayed.
  4. Check to see if one or more instances of notifyWindow exist and if so, passes the newly created notification agents a pointer to the instance previous in line. If no previous instances exist, the newly created notification agent's setTop() method is called which will cause it to center itself in the page.
  5. The new array element populated with the newly created notification agent's ID and pointer are added into the notifier object's array initialized when the object was created.

The kill() class method takes an ID as input and then iterating through the array of instantiated notification agents, calls the identified instance's close() method, deletes the notification agent object and then removes the ID/pointer array element from the list of instantiated notification agents.

Notification agent code...

Notification agent code

The code for the notification agents themselves is a bit longer and somewhat more complex than for the manager/factory class.

To simplify the explanation of the notification agents' code, each section/class method will be described separately with the code in its entirety shown at the end.

Notification agent class constructor...

Notification agent class constructor

function notifyWindow ()

Function called in conjuction with Javascript's new to instantiate a notification agent object.

Notification agent class members...

Notification agent class variables and methods

var myTarget = 0;
var myNotify = null;
var myPrev = null;
var myNext = null;
var scrollTop = getScrollTop();
var scrollLeft = getScrollLeft();
var winWidth = getWinWidth();
var winHeight = getWinHeight();
this.open = open;
this.setPrev = setPrev;
this.setNext = setNext;
this.setTop = setTop;
this.slideTop = slideTop;
this.close = close;

Class variables :

  • myTarget - During a position change, contains the target Y position.
  • myNotify - A pointer to the HTML page elements comprising the notification agent.
  • myPrev - If it exists, a pointer to the notification agent just above the current agent.
  • myNext - If it exists, a pointer to the notification agent just below the current agent.
  • scrollTop - The Y position of the page at the top of the current viewport. (Note 1)
  • scrollLeft - The X position of the page at the left of the current viewport. (Note 1)
  • winWidth - The width of the current viewport. (Note 2)
  • winHeight - The height of the current viewport. (Note 2)

Note 1 : The functions getScrollTop() and getScrollLeft() are helper functions implemented using concepts and techniques documented at Quirksmode's Viewport properties.

Note 2 : The functions getWinWidth() and getWinHeight() are helper functions implemented using concepts and techniques documented at Quirksmode's Window manipulation.

Class methods :

  • open - Called to pass the contents to be displayed to the newly instantiated notification agent . The construction of the actual HTML elements comprising the notification agent also takes place during this call.
  • setPrev - Used to pass the notification agent a pointer to the agent just above or null if none exists. If the parameter passed in, prev, is not null, the pointer is used in a call to the pointed to agent's set()Next method.
  • setNext - Used to pass the notification agent a pointer to the agent just below or null if none exists. If the parameter passed in, next, is not null, the pointer is used depending on current conditions to either cause the next agent to instantly position itself at the bottom of the current agent as in the case of the next agent having been newly instatiated or, to slide up to the bottom of this agent as would be the case where an intermediate agent is removed and the next in line is slid up to fill the newly created gap.
  • setTop - Causes the agent upon which is called to instantly place itself, the position of which is situation dependant. If the passed in parameter is null, an empty string or undefined, meaning it is empty, the agent will center itself in the viewport. If however there is an actual value passed in, it will place itself at that position. In either case, if next agent exists, it will in turn cause that agent to position itself at the new location of its new bottom position.
  • slideTop - Checks its current position against the target position and if not positioned accordingly, adjusts its vertical position upwards 2 pixel and sets a timer to continue the slide 25 milliseconds later. If the current position is roughly correct it will then position itself exactly at the targeted location. In either case it will cause the next notification agent to position itself at its new bottom position.
  • close - If there are both a next and a previous agent, the next's setPrev() is called with a pointer to the previous essentially removing the current notification agent from the linked agents. If on the other hand there is a previous but no next, it will call the previous's setNext() with a value of null. Conversely if there is a next but no previous, it will cause its next notification agent to slide up to its current position and set the next's previous to null.
Notification agent's open()...

Notification agent's open()

function open (text) {
  myNotify = document.createElement('div');
  myNotify.className = 'notifyContainer';
  myNotify.innerHTML = '<div class="notifyBody"><p>'+text+'</p></div>';
  document.getElementsByTagName('body')[0].appendChild(myNotify);
  myNotify.style.left = winWidth/2 + scrollLeft - myNotify.offsetWidth/2 + 'px';
}

This class method creates the HTML element that becomes the notice and inserts whatever text or HTML string passed in during the function call. This method will also cause the HTML element created to position itself in the center, width wise, of the view port.

The CSS class notifyContainer is defined in the CSS style sheet for this script so that the element is initialized with a CSS visibility value of hidden.

Notification agent's setPrev()...

Notification agent's setPrev()

function setPrev (prev) {
  myPrev = prev;
  if (myPrev != null)
    prev.setNext(this);
}

This class method sets the object's myPrev pointer and uses the pointer to notify the object pointed to that this object is now its next notification agent.

Notification agent's setNext()...

Notification agent's setNext()

function setNext (next) {
  if (myNext == null && next != null)
    next.setTop(myNotify.offsetTop + myNotify.offsetHeight);
  else if (next != null)
    next.slideTop(offsetTop + myNotify.offsetHeight);
  myNext = next;
}

If the notification agent's myNext has not yet been set, this will cause the next notification agent to instantly position itself just below this notification agent.

If myNext is currently set, indicating that there has been a change of what was the next notification agent, it will then cause the new next notification agent to slide to a position just below this notification agent.

Before returning from the call, the notification agent will set/update its myNext value.

Notification agent's setTop()...

Notification agent's setTop()

function setTop (top) {
  if (top == null || top == '' || top == 'undefined') {
    top = winHeight/2 + scrollTop - myNotify.offsetHeight;
  }
  myNotify.style.top = top + 'px';
  myNotify.style.visibility = 'visible';
  if (myNext != null) {
    myNext.setTop(top + myNotify.offsetHeight);
  }
}

This class method first checks to see if the parameter passed in, top, actually exists and if not, which would be the case when there are no other notification agents present, it will set itself vertically in the middle of the viewport.

The notification agent then sets its visibility to visible, it having been initialized to hidden, and then if a next notification agent exists, will call its setTop() method as well.

Notification agent's slideTop()...

Notification agent's slideTop()

function slideTop (top) {
  myTarget = top;
  doSlide();
}

This class method is more of a method stub than a full fledged function. Its primary purpose is to initiate a given notification agent's sliding process.

Notification agent's doSlide()...

Notification agent's doSlide()

function doSlide () {
  if (myNotify.offsetTop > myTarget) {
    myNotify.style.top = myNotify.offsetTop - 2 + 'px';
    setTimeout(doSlide, 25);
  } else {
    myNotify.style.top = myTarget + 'px';
  }
  if (myNext != null)
    myNext.setTop(myNotify.offsetTop + myNotify.offsetHeight);
}

This class method perfoms the bulk of the notification agents' repositioning process by performing the following steps :

  1. Check to see if the notification agent has yet reached its target position.
  2. If the target position has not been reached, move the agent up 2 pixels and set the timer to do it again 25 milliseconds later.
  3. If the target position has been reached or exceeded, (re)position the agent exactly at the target position.
  4. If there is a next notification agent, cause it to position itself so as to align its top with the bottom of the current agent.
Notification agent's close()...

Notification agent's close()

function close () {
  if (myPrev != null && myNext != null) {
    myNext.setPrev(myPrev);
  } else if (myPrev != null) {
    myPrev.setNext(null);
  } else if (myNext != null) {
    myNext.slideTop(myNotify.offsetTop);
    myNext.setPrev(null);
  }
  myNotify.parentNode.removeChild(myNotify);
}

This class method basically removes a given notification agent from the chain of notifications by setting its previous agent's next to its current next and vice versa.

After the previous and next have been introduced to each other, the notification agent will then remove its HTML elements from the page and upon returning from the call to close(), will be deleted by the manager/factory.

Notification agent, complete...

Notification agent, all together now

function notifyWindow () {
  var myTarget = 0;
  var myNotify = null;
  var myPrev = null;
  var myNext = null;
  var me = this;
  var scrollTop = getScrollTop();
  var scrollLeft = getScrollLeft();
  var winWidth = getWinWidth();
  var winHeight = getWinHeight();
  this.open = open;
  this.setPrev = setPrev;
  this.setNext = setNext;
  this.setTop = setTop;
  this.slideTop = slideTop;
  this.close = close;
  function open (text) {
    myNotify = document.createElement('div');
    myNotify.className = 'notifyContainer';
    myNotify.innerHTML = '<div class="notifyBody"><p>'+text+'</p></div>';
    document.getElementsByTagName('body')[0].appendChild(myNotify);
    myNotify.style.left = winWidth/2 + scrollLeft - myNotify.offsetWidth/2 + 'px';
  }
  function setPrev (prev) {
    myPrev = prev;
    if (myPrev != null)
      prev.setNext(this);
  }
  function setNext (next) {
    if (myNext == null && next != null)
      next.setTop(myNotify.offsetTop + myNotify.offsetHeight);
    else if (next != null)
      next.slideTop(offsetTop + myNotify.offsetHeight);
    myNext = next;
  }
  function setTop (top) {
    if (top == null || top == '' || top == 'undefined') {
      top = winHeight/2 + scrollTop - myNotify.offsetHeight;
    }
    myNotify.style.top = top + 'px';
    myNotify.style.visibility = 'visible';
    if (myNext != null) {
      myNext.setTop(top + myNotify.offsetHeight);
    }
  }
  function slideTop (top) {
    myTarget = top;
    doSlide();
  }
  function doSlide () {
    if (myNotify.offsetTop > myTarget) {
      myNotify.style.top = myNotify.offsetTop - 2 + 'px';
      setTimeout(doSlide, 25);
    } else {
      myNotify.style.top = myTarget + 'px';
    }
    if (myNext != null)
      myNext.setTop(myNotify.offsetTop + myNotify.offsetHeight);
  }
  function close () {
    if (myPrev != null && myNext != null) {
      myNext.setPrev(myPrev);
    } else if (myPrev != null) {
      myPrev.setNext(null);
    } else if (myNext != null) {
      myNext.slideTop(myNotify.offsetTop);
      myNext.setPrev(null);
    }
    myNotify.parentNode.removeChild(myNotify);
  }
}
Sample code download...

Sample code download

You can download a ZIP compressed file containing the HTML, Javascript and CSS style sheet files needed to display and example of this code's project in operation.