erin n pierce // creativity from the ground up


Adding a Scrollbar to a Dynamicly Populated Menu List in ActionScript 2.0

Hello people. Yesterday I made a break through. I had been struggling for a few days, attempting to think through how on earth I was going to add a custom scrollbar to TWO dynamically populated menu lists. Well, I solved it. First I found the code I needed and a sample file on kirupa.com (they rock btw).

Before the scrolling, I had an XML document with categories and projects. My code loaded the XML, parsed it and duplicated my menu item (nav button) using the duplicateMovieClip() method. I had it nested in a for loop and so it did all of the naming and depth placement at once. When you clicked on a menu item, a second function was called to populate the second list, passing through the category number and using the same duplicateMovieClip() method.

After looking at the way kirupa.com set up the scrollbar file I realized I needed to put both of my menu lists into container movie clips. That way I would be able to get the _y and _height properties of the grouped nav buttons. I’ve been working a lot with AS 3.0 and needed a refresher in AS 2 on how to nest duplicated items inside a movie clip. I Googled and found this article on actionscript.org reminding me you must use the attachMovie() method.

First I put both nav buttons into their own movie clips and call them: listOneHolder and listTwoHolder. Then I changed both duplicateMovieClip() methods to attachMovie(). Now the code looked like this:

function loadXML(loaded)
{
if (loaded)
{
xml = this.firstChild;
totalSections = xml.childNodes.length;
for (i = 0; i < totalSections; i++)
{
//duplicateMovieClip(myNav, ‘myNav’+i, i + 10); //OLD duplicate nav buttons
_root.listOneHolder.attachMovie(‘myNav’, ‘myNav’ + i, i + 10); //new attach movies

setProperty(_root.listOneHolder['myNav'+i], _x, navX); //set the x property
setProperty(_root.listOneHolder['myNav'+i], _y, navY); //set the y property

_root.listOneHolder['myNav'+i].navTxt.htmlText = xml.childNodes[i].attributes.name; // set the text value
}
}
}

note: in addition to having my two menu item movie clips on the stage (myNav and btn), i made sure to right click on them in the library, select “Linkage…” and clicked “Export for ActionScript”.

after that i was able to start tackling the actual scrollbar issue. i figured out that i could use the code from kirupa.com (as seen below) and modify it to accommodate for my two lists.

// KIRUPA //
function scrolling () {
var scrollHeight:Number = theScrollbar.scrollTrack._height;
var contentHeight:Number = contentMain._height;
var scrollFaceHeight:Number = theScrollbar.scrollFace._height;
var maskHeight:Number = maskedView._height;
var initPosition:Number = theScrollbar.scrollFace._y=theScrollbar.scrollTrack._y;
var initContentPos:Number = contentMain._y;
var finalContentPos:Number = maskHeight-contentHeight+initContentPos;
var left:Number = theScrollbar.scrollTrack._x;
var top:Number = theScrollbar.scrollTrack._y;
var right:Number = theScrollbar.scrollTrack._x;
var bottom:Number = theScrollbar.scrollTrack._height-scrollFaceHeight+theScrollbar.scrollTrack._y;
var dy:Number = 0;
var speed:Number = 10;
var moveVal:Number = (contentHeight-maskHeight)/(scrollHeight-scrollFaceHeight);

theScrollbar.scrollFace.onPress = function() {
var currPos:Number = this._y;
startDrag(this, false, left, top, right, bottom);
this.onMouseMove = function() {
dy = Math.abs(initPosition-this._y);
contentMain._y = Math.round(dy*-1*moveVal+initContentPos);
};
};
theScrollbar.scrollFace.onMouseUp = function() {
stopDrag();
delete this.onMouseMove;
};
theScrollbar.btnUp.onPress = function() {
this.onEnterFrame = function() {
if (contentMain._y+speed
if (theScrollbar.scrollFace._y<=top) {
theScrollbar.scrollFace._y = top;
} else {
theScrollbar.scrollFace._y -= speed/moveVal;
}
contentMain._y += speed;
} else {
theScrollbar.scrollFace._y = top;
contentMain._y = maskedView._y;
delete this.onEnterFrame;
}
};
};
theScrollbar.btnUp.onDragOut = function() {
delete this.onEnterFrame;
};
theScrollbar.btnUp.onRelease = function() {
delete this.onEnterFrame;
};
theScrollbar.btnDown.onPress = function() {
this.onEnterFrame = function() {
if (contentMain._y-speed>finalContentPos) {
if (theScrollbar.scrollFace._y>=bottom) {
theScrollbar.scrollFace._y = bottom;
} else {
theScrollbar.scrollFace._y += speed/moveVal;
}
contentMain._y -= speed;
} else {
theScrollbar.scrollFace._y = bottom;
contentMain._y = finalContentPos;
delete this.onEnterFrame;
}
};
};
theScrollbar.btnDown.onRelease = function() {
delete this.onEnterFrame;
};
theScrollbar.btnDown.onDragOut = function() {
delete this.onEnterFrame;
};

if (contentHeight
theScrollbar.scrollFace._visible = false;
theScrollbar.btnUp.enabled = false;
theScrollbar.btnDown.enabled = false;
} else {
theScrollbar.scrollFace._visible = true;
theScrollbar.btnUp.enabled = true;
theScrollbar.btnDown.enabled = true;
}
};

In order to accommodate for my two lists I needed to modify the function to have a few parameters passed through … those were the scrollbar instance (i put two on my stage, one for each list), the mask instance (again, two on each stage, one for each list), and the list. So my new function looked like this:

function goScrolling (_mc, mask_mc, listHolder_mc)
{
var scrollHeight:Number = _mc.scrollTrack._height;
var contentHeight:Number = listHolder_mc._height;
var scrollFaceHeight:Number = _mc.scrollFace._height;
var maskHeight:Number = mask_mc._height;
var initPosition:Number = _mc.scrollFace._y = _mc.scrollTrack._y;
var initContentPos:Number = listHolder_mc._y;
var finalContentPos:Number = maskHeight – contentHeight + initContentPos;
var left:Number = _mc.scrollTrack._x;
var top:Number = _mc.scrollTrack._y;
var right:Number = _mc.scrollTrack._x;
var bottom:Number = _mc.scrollTrack._height – scrollFaceHeight + _mc.scrollTrack._y;
var dy:Number = 0;
var speed:Number = 10;
var moveVal:Number = (contentHeight – maskHeight) / (scrollHeight-scrollFaceHeight);

_mc.scrollFace.onPress = function()
{
var currPos:Number = this._y;
startDrag(this, false, left, top, right, bottom);
this.onMouseMove = function()
{
dy = Math.abs(initPosition – this._y);
listHolder_mc._y = Math.round(((dy * -1) * moveVal) + initContentPos);
}
}
_mc.scrollFace.onMouseUp = function()
{
stopDrag();
delete this.onMouseMove;
}
_mc.btnUp.onPress = function()
{
this.onEnterFrame = function()
{
if (listHolder_mc._y + speed < mask_mc._y)
{
if (_mc.scrollFace._y <= top)
{
_mc.scrollFace._y = top;
}
else
{
_mc.scrollFace._y -= speed / moveVal;
}
listHolder_mc._y += speed;
}
else
{
_mc.scrollFace._y = top;
listHolder_mc._y = mask_mc._y;
delete this.onEnterFrame;
}
}
}
_mc.btnUp.onDragOut = function()
{
delete this.onEnterFrame;
}
_mc.btnUp.onRelease = function()
{
delete this.onEnterFrame;
}
_mc.btnDown.onPress = function()
{
this.onEnterFrame = function()
{
if ((listHolder_mc._y – speed) > finalContentPos)
{
if (_mc.scrollFace._y >= bottom)
{
_mc.scrollFace._y = bottom;
}
else
{
_mc.scrollFace._y += speed / moveVal;
}
listHolder_mc._y -= speed;
}
else
{
_mc.scrollFace._y = bottom;
listHolder_mc._y = finalContentPos;
delete this.onEnterFrame;
}
}
}
_mc.btnDown.onRelease = function()
{
delete this.onEnterFrame;
}
_mc.btnDown.onDragOut = function()
{
delete this.onEnterFrame;
}

if (contentHeight < maskHeight)
{
_mc._visible = false;
}
else
{
_mc._visible = true;
}
}

Once I had that modified, I needed to figure out when to call the function. First I called it right away. The first menu populates and at the end of my XML loader function I call the scrolling function to check to see if either list needs a scrollbar. Then, once one of the categories is selected (from list one), I needed to call the scrolling function again, checking to see if list two needs a scrollbar.

So, the first time I call it, inside my XML loader function, I am passing through the scrollbar for list one, the mask for list one and list one mc:

goScrolling(_root.theScrollbar1, _root.maskedView1, _root.listOneHolder);

The second time I call the function, inside my XML loader function, inside the onRelease function for my main menu (list one) nav i pass through the second list, mask and scrollbar:

_root.listOneHolder['myNav'+i].onRelease = function ()
{
goScrolling(_root.theScrollbar2, _root.maskedView2, _root.listTwoHolder);
}

I then had two functioning scrollbars! However, I have run into two problems … one of which I have solved, the other won’t take long but I have not yet done. I clicked on one of the main menu items. List two was long enough to have a scrollbar and I then scrolled to the bottom and selected one of the projects (or secondary menu items). When I went to another category, I realized list two did not reset itself. The _y property was still at the same value, as if I had used the scrollbar. SO. I added to the end of the scrolling function a short bit of logic:

// the number 55 represents the starting _y value
if (initContentPos < 55)
{
listHolder_mc._y = 55;
}

Here’s a link to the project as it is today. The second issue I found has to do with the mathematical calculation of height and/or how far down the scroll bar needs to go. The scroll current goes too far down, or rather, sets the _y property too low. So now I am off to fix that. I’ll post in the comments the solution when I have it.

Good luck!

Bookmark and Share

One Response to “Adding a Scrollbar to a Dynamicly Populated Menu List in ActionScript 2.0”

  1. Erin Pierce says:

    SOLUTION to height problem … for some reason it was not passing the correct height when calculating the listHolder_mc._height. SO, i added a fourth variable to be passed through to the scrolling function. In my for loops I created a variable called listOneHeight and listTwoHeight and set it equal to the menu item height multiplied by the number of items:

    // inside the for loop
    navH = _root.listOneHolder.myNav._height + spacing; // var spacing set earlier to what ever distance you want between buttons
    listOnefinalH = navH * i; // actual list menu height
    // the updated scrolling function now has a finalH variable being passed through and then set the contentHeight equal to that
    var contentHeight:Number = finalH;

    // and in the second list’s for loop
    btnH = _root.listTwoHolder.btn._height + spacing; // var spacing set earlier to what ever distance you want between buttons
    listTwoFinalH = btnH * i; // actual list menu height
    // the updated scrolling function now has a finalH variable being passed through and then set the contentHeight equal to that

    // inside my scrolling function
    var contentHeight:Number = finalH;

Leave a Reply

name*

email* (will not be published)

website

comments