Self contained system for color transition

  • Thread starter Richard A. DeVenezia
  • Start date
R

Richard A. DeVenezia

I hope this is the end of my present 'discovery' phase. I've learned alot
about JavaScript in a short time and my head hurts. The following is what
came out of all my questions and all the excellent answers (thanks!). It
will be the basis of a slightly more complicated function for rendering a
two-level navigation bar ( Don't have time to get into design of a
multi-styletype renderer for n-level hierarchies. )

This is a single function that will perform color transitioning.
If Foo is an unavailable identifier, change it to whatever you want, nothing
else needs to be changed (except the invocation of it)

----
function Foo (argument) {
// Simpleton singleton
var callee = arguments.callee

if (callee.singleton != undefined)
return callee.singleton

if (this == window)
return new callee ( argument )

callee.singleton = this

var myName = callee.toString().replace (/\n/g, ' ').match(
/function\s(.*?)(\s|\()/ ) [1]
//alert ('my name is ' + myName)

installClassMethods ()

doBunchOfStuff()
return

function doBunchOfStuff () {
var id = '_' + Math.random(0).toString().substring(2)
document.write ('<TABLE ID="' + id + '"><TR><TD></TD></TR></TABLE>')
var table = document.getElementById (id)
table.deleteRow (0)
var row = table.insertRow (table.rows.length)
var cell = row.insertCell ( row.cells.length )

cell.innerHTML = '<B>Provided</B> by <I>convenience</I> of innerHTML'

cell.style.borderStyle = 'solid'
cell.style.borderColor = 'Black'
cell.style.borderWidth = '1px'

cell.onmouseover = function ( E ) { callee.over (this) }
cell.onmouseout = function ( E ) { callee.out (this) }
}

//--------------------------------------------------------------
function installClassMethods () {
//------------------------------------------------------------
callee.over = function (td) {
td.style.backgroundColor = 'black'
}
callee.out = function (td) {
td.transition = new callee.transition (td)
td.transition.doStart()
}
callee.transition = function (td) {
this.td = td
this.step = 0
this.interval = 10
}
callee.transition.prototype.doStart = function () {
thisObj = this
thisObj.doStep()
this.timerId = setInterval ( function () { thisObj.doStep() },
this.interval )
}
callee.transition.prototype.doStep = function () {
this.step += 4
if (this.step > 255) this.step = 255
this.td.style.backgroundColor =
'rgb('+this.step+','+this.step+','+this.step+')'
if (this.step == 255) {
clearTimeout (this.timerId)
this.td.transition = null
}
}
}
} // end of Foo

Foo('Hi')
 
R

Richard Cornford

function Foo (argument) {
// Simpleton singleton
var callee = arguments.callee

if (callee.singleton != undefined)
return callee.singleton

if (this == window)
return new callee ( argument )

callee.singleton = this

var myName = callee.toString().replace (/\n/g, ' ').
match( /function\s(.*?)(\s|\()/ ) [1]
//alert ('my name is ' + myName)

installClassMethods ()

doBunchOfStuff()

As - doBunchOfStuff - is and inner function of this function and is only
called once does it need to be an inner function at all?
return

function doBunchOfStuff () {
var id = '_' + Math.random(0).toString().substring(2)
document.write ('<TABLE ID="' + id +
'"> said:
var table = document.getElementById (id)

document.getElementById - may be widely implemented but it is not
universal. It is safer to test for the existence of this type of
function before using them to avoid error reports and allow for
controlled fall-back and clean degradation.
table.deleteRow (0)

deleteRow - may be HTML DOM specified and widely implemented but it is
not universal (even in browsers that are otherwise quite good DOM
implementations). You should test that method exists before using it to
avoid error reports and allow for controlled fall-back and clean
degradation. One fall-back option might be the DOM core - removeChild -
function called on the implied TBODY element.
var row = table.insertRow (table.rows.length)
var cell = row.insertCell ( row.cells.length )

Similarly insertRow and insertCell.
cell.innerHTML = '<B>Provided</B> by <I>convenience</I> of
innerHTML'

The test for innerHTML support is - if(typeof cell.innerHTML ==
'string') - though it may be implemented as read only (eg. Web Browser
2.0 on Palm OS as I recall (I don't have its docs to hand)). DOM node
insertion methods are more widely supported and, if implemented, will do
the job, so they could be the primary method with a fall-back to
innerHTML or the other way around.
cell.style.borderStyle = 'solid'
cell.style.borderColor = 'Black'
cell.style.borderWidth = '1px'

cell.onmouseover = function ( E ) { callee.over (this) }
cell.onmouseout = function ( E ) { callee.out (this) }

IE browsers are unable to garbage collect DOM elements and JavaScript
objects that form circular references with one another. Forming such
circular references produces a memory leak that persists until the
browser is shut down. Assigning these inner functions to properties of
the TD means that the TD has a reference to the functions, the functions
hold a reference to the closures that assigning the functions produces
and that closure has a reference to the TD held in the - cell -
property. A circular reference. At this point in the code this problem
can be averted by actively clearing the references to the DOM nodes from
the cell, row and table local variables (eg. cell = null; etc. at the
end of the function).
}

//--------------------------------------------------------------
function installClassMethods () {
//------------------------------------------------------------
callee.over = function (td) {
td.style.backgroundColor = 'black'
}
callee.out = function (td) {
td.transition = new callee.transition (td)
td.transition.doStart()
}
callee.transition = function (td) {
this.td = td
<snip>

That is another circular reference. The - new callee.transition(td) -
object is assigned as an expando property on the TD so the TD refers to
it, and the object holds a reference to the TD which it is a property
of. This circular reference is harder to break and would require the
expando property to be cleared. I notice that you are doing that later
on so this may not be a real problem (unless the page is unloaded before
the end of the sequence), but I don't see the need for the expando at
all. What would be wrong with:-

callee.out = fuction(td){
new callee.transitin(td).doStart();
};

- and drop the - this.td.transition = null - line from the doStep
function.

However, the event handling functions do not need to be inner functions
that call the public static functions of the constructor. You could just
assign the equivalent functions directly to the event handlers:-

cell.onmouseover = function(E){
this.style.backgroundColor = 'black'
};
cell.onmouseout = function(E){
new callee.transition(this).doStart()
};

- Obviously the references to the TD have changed from a passed
reference to the use of he - this - keyword (and I have removed the
expando) but otherwise the functions do the same task.

Also, although - new callee.transtion(this) - will work I don't think
that you need the transition constructor to be a property of the -
callee - constructor at all. If - transition - was an inner function
declaration:-

function Transition(td){
this.td = td;
this.step = 0;
this.interval = 10;
}

(or function expression - var Transition = function(td){ ...} -)

It would be possible to assign functions to its prototype (eg):-

Transition.prototype.doStart = function (){
thisObj = this;
thisObj.doStep();
this.timerId =
setInterval ( function () { thisObj.doStep() },this.interval );
};

- and use it as a constructor - new Transition(this); - wherever its
identifier was within the scope, ie. from within the "Foo" constructor
and any of its inner functions (such as the one assigned to
cell.onmouseout (the function that actually constructs the Transition
objects)).

Given your desire to keep the code independent of the identifier used
for the constructor, my pattern for this object would be:-

var Foo = (function(){
var singleton = null;
function Transition(){
. . .
}
Transition.prototype.doStart = function (){
. . .
};
Transition.prototype.doStep = function (){
. . .
};
return (function(argument){ //this is the singleton's constructor.
if (singleton)return singleton;
if (this == window){
return new arguments.callee( argument );
}
singleton = this
. . .
cell.onmouseover = function(E){
this.style.backgroundColor = 'black'
};
cell.onmouseout = function(E){
new Transition(this).doStart()
};
. . .
cell = null;
. . .
});
})(); //inline function expression execution
//assigning the singleton constructor to
//any identifier required.

- and now "Foo" has no public static properties at all.

Richard.
 
R

Richard A. DeVenezia

Richard Cornford said:
Given your desire to keep the code independent of the identifier used
for the constructor, my pattern for this object would be:-

var Foo = (function(){
var singleton = null;
function Transition(){
. . .
}
Transition.prototype.doStart = function (){
. . .
};
Transition.prototype.doStep = function (){
. . .
};
return (function(argument){ //this is the singleton's constructor.
if (singleton)return singleton;
if (this == window){
return new arguments.callee( argument );
}
singleton = this
. . .
cell.onmouseover = function(E){
this.style.backgroundColor = 'black'
};
cell.onmouseout = function(E){
new Transition(this).doStart()
};
. . .
cell = null;
. . .
});
})(); //inline function expression execution
//assigning the singleton constructor to
//any identifier required.

Thanks for the pattern. I think I've seen it before, but this is more
digestable than those times.

Would it matter if the prototype assignment came after the return ? I'm
thinking it's yes, although the Transition function probably could be after
(since its declared as a function and not a var assigned to an anonymous
function)
As - doBunchOfStuff - is and inner function of this function and is only
called once does it need to be an inner function at all?

Well, it's about 470 lines of code that examines, parses and reports errors
regarding FooData that is passed in.
The nesting is gone (I used to rely of inheriting some vars from the parent
functions, but now I have ~10 top-level function vars.
They maintain counts and states needed at various multiple points.
The parsing looks like
bunch of lines
loop1
bunch of lines
loop2
bunch of lines
end2
end1
where ever I had bunch of lines in a loop, I moved them out to a function
which is then called from inside a now 'tight loop'

Most browsers I get are ie. I'm willing to leave a few souls in the dark,
but will test pages versus latest ie,moz & opera.
DOM node
insertion methods are more widely supported and, if implemented, will do
the job, so they could be the primary method with a fall-back to
innerHTML or the other way around.

DOM... but I have such little hair left to pull out.
I think before I travel down the 'code that generates correctly nearly
universally on the client side' road, I would investigate server side
generation.
property. A circular reference. At this point in the code this problem
can be averted by actively clearing the references to the DOM nodes from
the cell, row and table local variables (eg. cell = null; etc. at the
end of the function).

Excellent advice... thanks.
all. What would be wrong with:-

callee.out = fuction(td){
new callee.transitin(td).doStart();
};

- and drop the - this.td.transition = null - line from the doStep
function.

However, the event handling functions do not need to be inner functions
that call the public static functions of the constructor. You could just
assign the equivalent functions directly to the event handlers:-

cell.onmouseover = function(E){
this.style.backgroundColor = 'black'
};
cell.onmouseout = function(E){
new callee.transition(this).doStart()
};

I would love to not need to attached transition to the element, however in
the 'bigger' version, I have to stop an elements transition if the mouse
moves over the element again. I've got to store the timerId somewhere so
that I can stop prematurely.
Also, although - new callee.transtion(this) - will work I don't think
that you need the transition constructor to be a property of the -
callee - constructor at all. If - transition - was an inner function
declaration:-

function Transition(td){
this.td = td;
this.step = 0;
this.interval = 10;
}

Good point, however I find out that Mozilla doesn't do timered stuff with
functions, just strings (grrrr). So if I want mozilla to transition I would
need at least on Foo constructor function property for dispatching the event
handling.
 
R

Richard Cornford

Richard A. DeVenezia said:
Would it matter if the prototype assignment came after the
return ? I'm thinking it's yes, although the Transition function
probably could be after (since its declared as a function and
not a var assigned to an anonymous function)

Yes it matters, function expressions (as distinct from function
declarations) are evaluated in source doe order so if they follow the
return statement then they will never be evaluated. See the ECMA
specification for the difference between function declarations and
expressions, and the section on the creation of the "variable" object in
the execution context.
Well, it's about 470 lines of code that examines, parses and
reports errors regarding FooData that is passed in.

OK, familiarise yourself with the distinction between function
expressions and declarations (and the creation of the "variable"
object), as you may find that you are needlessly repeatedly creating a
very large function object each time the singleton constructor is called
(as - doBunchOfStuff - is an inner function declaration in your original
code).

Most browsers I get are ie.

How can you tell?
I'm willing to leave a few souls in
the dark, but will test pages versus latest ie,moz & opera.

It seems perverse to put so much effort into the OO style and patterns
of your code and then apply such low standards to the browser scripts
you create with it.

One of the probable outcomes of your script erroring is that JavaScript
execution for the page will be terminated. That would mean that the
failure of a script that represents an optional visual effect is
impacting on any other scripts on the page when none of those scripts
may have any real dependency on your script or on the absence of the
objects/functions that are causing your script to error.

I think that it is in broad accordance with an OO approach that
JavaScript Objects designed for use within a browser should encapsulate
the consequences of their failure as well as the details of their
operation.

DOM... but I have such little hair left to pull out.

It isn't that difficult.
I think before I travel down the 'code that generates
correctly nearly universally on the client side' road, I
would investigate server side generation.

They aren't mutually exclusive. "universal" JavaScript is impossible (as
some browsers have JavaScript disabled/unavailable) but client side can
ease the server load, provide optional extras and enhancements and
having server scripts to fall back to in the event of client-side
failure can produce the ultimate in cross-browser and reliable code.

I would love to not need to attached transition to the element,
however in the 'bigger' version, I have to stop an elements
transition if the mouse moves over the element again. I've got
to store the timerId somewhere so that I can stop prematurely.

It is the circular object references that cause the IE memory leak but
the value returned from setTimeout/Interval is just a value (only
meaningful to clearTimeout/Interval). You could assign that value to the
TD's expando if that is the only value that needs to be associated with
the TD itself.

... , however I find out that Mozilla doesn't do timered
stuff with functions, just strings (grrrr). So if I want mozilla to
transition I would need at least on Foo constructor function
property for dispatching the event handling.

Well you now know that Mozilla is fine with the function reference
argument but you could still look into my toString based fallback
method. That does require a global reference to be available (possibly
as a property of "Foo") but it only needs to be created in the event
that the fallback is needed (it could be assigned within the toString
method and thus not used if function references are recognised).

Richard.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,085
Messages
2,570,597
Members
47,219
Latest member
Geraldine7

Latest Threads

Top