This example is for anyone who needs to use anchor tags in ActionScript TextFields. If using AIR than the best route is to use an HTMLLoader to load in the raw HTML. If an HTMLLoader is not an option you can use this example to create a wrapper class that mimics the anchor tag functionality. Before we get started it is important to note that TextFields have a number of properties, formats and styles available which all have an effect on the way text is rendered. This example takes advantage of a specific set of properties in order to accurately determine the line number and char location of certain strings. Variables may require some adjustments / tweaks (or may not work at all) if the TextField properties are changed.
To get started lets take a look at the HTML we plan to load in:
<b>Index</b> <a href="#sectionA"><u>Jump to Section A</u></a> <a href="#sectionB"><u>Jump to Section B</u></a> <a href="#sectionC"><u>Jump to Section C</u></a> <a href="#sectionD"><u>Jump to Section D</u></a> <a href="#sectionE"><u>Jump to Section E</u></a> <span name="sectionA"><b>Section A</b></span> <p>...</p> <p>...</p> <a href="#top">Back to Top</a> <span name="sectionB"><b>Section B</b></span> <p>...</p> <p>...</p> <a href="#top">Back to Top</a> <span name="sectionC"><b>Section C</b></span> <p>...</p> <p>...</p> <a href="#top">Back to Top</a> <span name="sectionD"><b>Section D</b></span> <p>...</p> <p>...</p> <a href="#top">Back to Top</a> <span name="sectionE"><b>Section E</b></span> <p>...</p> <p>...</p> <a href="#top">Back to Top</a>
The content of the paragraph files has been removed to save space (you can view the source below to get a copy of the full file). Notice that the ‘a name’ tags have been replaced with ‘span name.’ This was a quick way to prevent the styling of the link tags from modifying the style of the headers. There is probably a better way to get around this but it’s not within scope of this example. Speaking of the style sheet… here it is:
/* CSS file */ a:hover { color: #0000FF; } a:link { color: #555555; text-decoration: underline; } a:active { color: #FF0000; } a:visited { color: #555555; text-decoration: underline; }
Lets start by loading in the HTML text:
import com.cb.util.AnchorText; import flash.text.TextField; import flash.events.TextEvent; private var _htmlLoader:URLLoader = new URLLoader(); private var html_url:String = "assets/test.html"; private var myText:AnchorText; private function init():void { _htmlLoader.addEventListener(Event.COMPLETE, htmlLoadHandler); _htmlLoader.load(new URLRequest(html_url)); } private function htmlLoadHandler(event:Event):void { myText = new AnchorText(_htmlLoader.data, 300); myTextComponent.addChild(myText); }
Now lets take a look at some of the key functions within the Anchor Text class. First we will want to replace the a href # tags with something ActionScript can understand:
// Replaces '#' with 'event:' to trigger actionscript events protected function createAnchors():void { var regEx:RegExp = new RegExp("<a href=\"#", "ig"); _text = _text.replace(regEx, "<a href=\"event:"); }
Next we will want to add event listeners for the event and set up our dynamic text field:
// Constructor public function AnchorText(text:String, width:Number = 200, height:Number = 300) { _width = width; _height = height; _text = text; // Add event dispatching to the a href tags createAnchors(); _dynamicText = new TextField(); _dynamicText.addEventListener(TextEvent.LINK, anchorText); this.addChild(_dynamicText); _styleSheet = new StyleSheet(); // Comment out the following three lines if you don't have an external stylesheet _styleLoader = new URLLoader(); _styleLoader.addEventListener(Event.COMPLETE, styleLoadHandler); _styleLoader.load(new URLRequest(css_url)); updateFormat(); } // Set up the TextField protected function updateFormat():void { _dynamicText.width = _width; _dynamicText.height = _height; _dynamicText.styleSheet = _styleSheet; _dynamicText.wordWrap = true; _dynamicText.htmlText = _text; _dynamicText.selectable = true; // Text MUST be selectable }
Finally we will want to handle the mouse clicks to the events that we set up within the text string:
protected function anchorText(event:TextEvent):void { // Recognize 'back to top' links if(event.text == "top"){ _dynamicText.scrollV = 0; return; } // Set up ghost text field to match properties of the current one var anchorText:TextField = new TextField(); anchorText.wordWrap = true; anchorText.width = _width; anchorText.styleSheet = _styleSheet; // This is REQUIRED // Replace the name tag with a unique pattern (^#^) var regEx:RegExp = new RegExp("<span name=\""+ event.text +"\">", "ig"); anchorText.htmlText = _text.replace(regEx, "^#^"); // Replace newlines with '&' to keep char count consistant regEx = new RegExp("^", "gm"); anchorText.htmlText = anchorText.htmlText.replace(regEx, "&"); // Remove duplicate newlines regEx = new RegExp("&&", "g"); anchorText.htmlText = anchorText.htmlText.replace(regEx, "x"); // Find the char location of the unique pattern var myIndex:int = anchorText.text.indexOf("^#^") + 3; // Set the scroll index to the line number of the char index var scrollIndex:int = _dynamicText.getLineIndexOfChar(myIndex) + 1; // Scroll to the necessary line _dynamicText.scrollV = scrollIndex; }
Some key things to note would be:
- The TextField MUST be selectable
- A StyleSheet must be applied to the TextField (even if you don’t load one in)
- This example assumes ‘^#^’ and ‘&&’ are unique sequences
- A TextFormat can not be used along with a StyleSheet
- There are always exception cases when dealing with RegExp and TextFields (no warranty provided)
View Sample / Source Files
Here are a few resources I found helpful along the way:
http://www.kirupa.com/forum/showthread.php?t=303186
http://troyworks.com/blog/2008/03/14/flash-textfield-actionscript-hyperlink-in-as30/
The textfield doesn’t have to be selectable, it just needs to have mouseEnabled set to true.