Flex :: Truncating HTML Text

With Flex truncating text within a Label control is easily done by setting the property “truncateToFit” to true. This parameter doesn’t do much when using the Text or TextArea controls in Flex. To truncate text in these controls you would have to build your own function to count characters and add the ellipses. There is one good example of creating your own custom Text control that truncates the text in the same manner as the Label control based on size of the control.

Now if you want to truncate HTML text within a Text or TextArea control thinks become more complicated. Currently in Flex doing anything with HTML is pretty dismal as there are not many ways to control the display of HTML or capture user interaction with HTML text. This seems to be changing with the upcoming release of Flash Builder 4 and the new spark components for displaying text and also the new Text Layout Framework . In the mean time you still might like to truncate HTML text in Text and TextArea controls.

I did find one blog post describing one method for truncating HTML text based on the size of the control. However, I wanted more restriction of the length of my HTML displayed and also wanted a way to better handle links that use the TextEvent to fire events from links clicks. There seem to be plenty of examples of HTML truncation within JavaScript and Java. I am lucky to have a girlfriend who also develops Java (and is also good looking :D ). I asked her for an example of HTML truncation and then translated that code in to working Flex code, which was surprisingly easy to convert.

Below is an example application that will truncate HTML text to any character length given. The truncation will not occur mid-link tag, only the text inside the links is counted you have the option to put a “… more” link at the end of the truncation to expand the text to its full length. I also gave the HTML a little extra formatting to distinguish links from regular text and add some rollover behavior for HTML links (something also missing from Flex). If the text Some limitations include image tags, break tags, and wrapping the entire block of text into a tag (However there is an easy work around that you can see in source code). The reason these limitations exist is because I only needed to truncate text that contain links. So the example will have to be expanded to include other tags.

Example

Complete Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="588" height="393" applicationComplete="init()">
    <mx:Script>
        <![CDATA[
           
            import flash.net.navigateToURL;
           
            // string data to play with
            private var loremIpsum:String = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
                        "Curabitur ultrices orci non felis luctus non sollicitudin magna aliquet." +
                        " Curabitur lacinia dignissim <a href=\"event:link:http://www.google.com\">accumsan</a>. " +
                        "Mauris ac dui in enim tristique egestas eu ac arcu. " +
                        "Quisque scelerisque, odio et luctus tempus, felis ante euismod felis, quis laoreet quam " +
                        "turpis in diam <a href=\"event:link:http://www.google.com\">http://www.google.com</a>. " +
                        "Integer ut lectus id justo feugiat posuere. Vestibulum tempor porttitor " +
                        "justo, sed consequat dui lobortis ut. <a href=\"event:link:http://www.google.com\">Aliquam</a> " +
                        "a posuere diam. Vestibulum turpis purus, " +
                        "dapibus id sagittis nec, volutpat ultricies leo. <a href=\"event:link:http://www.google.com\">Pellentesque</a> tempus pulvinar ornare. " +
                        "Cras vel sapien vitae mauris tincidunt <a href=\"event:link:http://www.google.com\">http://www.google.com</a>" +
                        " laoreet quis congue nisl. Aliquam pharetra nunc " +
                        "quis tortor adipiscing quis <a href=\"event:link:http://www.google.com\">elementum</a> nulla venenatis. " +
                        "Suspendisse potenti. Mauris " +
                        "vitae enim sed nisl viverra venenatis <a href=\"event:link:http://www.google.com\">http://www.google.com</a>. " +
                        "Proin auctor <a href=\"event:link:http://www.google.com\">mattis</a> mollis. Phasellus ultrices " +
                        "ornare ullamcorper. Sed <a href=\"event:link:http://www.google.com\">turpis</a> quam, tempus eget luctus eget, posuere vitae lorem. " +
                        "In vitae sem id lorem aliquam <a href=\"event:link:http://www.google.com\">viverra</a>."
       
            private function init():void
            {
                this.destinationText.styleSheet = this.getTextStyle();
                this.destinationText.addEventListener(TextEvent.LINK, handleLinkClick);
                this.destinationText.htmlText = "<span class=\"body\">" + loremIpsum + "</span>";
                this.sourceText.addEventListener(TextEvent.LINK, handleLinkClick);
                this.sourceText.styleSheet = this.getTextStyle();
                this.sourceText.htmlText = "<span class=\"body\">" + loremIpsum + "</span>";
                return;
            }
           
            private function truncateText():void
            {
                var limit:Number = limitStepper.value;
                this.destinationText.htmlText = "<span class=\"body\">" + this.truncateHTMLText(loremIpsum, limit, true) + "</span>";
                this.destinationText.styleSheet = this.getTextStyle();
                return;
            }
           
            /**
             * Handle the click action on links within the html text using TextEvent.
             * */

            private function handleLinkClick(event:TextEvent):void
            {
                var prefix:String = event.text;
                var split:Array = prefix.split(":");
                var type:String = split[0];
                var id:String = split[1];
               
                if(split.length > 2) id = split[1] + ":" + split[2]; // recreate the http link
               
                if(type == "more"){
                    this.destinationText.htmlText = "<span class=\"body\">"  + loremIpsum + "</span>"; // reset to original text length
                    this.destinationText.styleSheet = this.getTextStyle();
                    return;
                } else {
                    var request:URLRequest = new URLRequest(id);
                    navigateToURL(request,"_blank");
                }

                return;
            }
           
            /**
             * Truncate html text.
             * @param value The original text value
             * @param limit The maximum number of characters to show
             * @param ellipses  Boolean value to show elipses at the end of truncation.
             * */

            private function truncateHTMLText(value:String, limit:Number, ellipses:Boolean = true):String
            {
                if(limit == 0)
                    return "";
               
               var original:String = value;
               
               value = value.replace("[\\t\\n\\x0B\\f\\r\\u00A0]+", "");
               
                var isTag:Boolean = false;
                var count:int = 0;
                var position:int = 0;
                var limitLength:int = value.length - 1;
                var closeTag:Boolean = false;
               
                for(var i:int = 0; i < value.length; i++) {
                    var c:String = value.charAt(i);
                    if(isTag) {
                        if(c == '>') {
                            isTag = false;
                            if(closeTag || i == limitLength) {
                                position = i;
                                break;
                            }
                            continue;
                        } else {
                            continue;
                        }
                    } else {
                        if(c == '<') {
                            isTag = true;
                        } else {
                            count++;
                            if(i == limitLength || (count == limit)) {
                                if(((i+1) < limitLength) && ((i+2) < limitLength)) {
                                    if(value.charAt(i+1) == '<' && value.charAt(i+2) == '/') {
                                        closeTag = true;
                                        continue;
                                    }
                                }
                                position = i;
                                break;
                            }
                        }
                    }
                }
               
                var result:String = value.substring(0, position + 1);
                var last:String = result.charAt(result.length - 1);
                var length:int = result.length;
               
                var nextChar:String = (length >= value.length) ? ' ' : value.charAt(length);
               
                if(last != ' ' && last != '>' && nextChar != ' ' && nextChar != '<')
                    result = result.substring(0, result.lastIndexOf(' ') + 1);
                var lastStartTag:int = result.lastIndexOf('<');
               
                if(lastStartTag != -1) {
                    var ch:String = result.charAt(lastStartTag + 1);
                    if(ch != '/') {
                        result = result.substring(0, lastStartTag);
                    }
                }
               
                if(original.length == result.length)
                    return original;
   
                if(result.length == 0)
                    return result;
           
                var pattern:RegExp = new RegExp("(.*?)(\\s*\.\.\.\\s*)([\</[a-z]*?\>\\s*$]+)", "i");
               
                if(result.search(pattern) == -1 && ellipses)
                    result += "...";
   
                return (result +  "<a href=\"event:more\">[more]</a>");
            }
           
            /**
             * Let's add a little style to the HTML text and mouse roll-over actions.
             * */

            private function getTextStyle() : StyleSheet
            {
                var fonts:String = "Arial, _sans";
                var textStyle:StyleSheet = new StyleSheet();
                    textStyle.setStyle(".body", {fontFamily:fonts, fontSize:"12", fontWeight:"normal", color:"#222222", textDecoration:"none"});
                    textStyle.setStyle("a", {color:"#245290", textDecoration:"none"});
                    textStyle.setStyle("a:link", { textDecoration: "none", color: "#245290" });
                    textStyle.setStyle("a:hover", { textDecoration: "underline" });
               
                return textStyle;
            }
        ]]>
    </mx:Script>
   
    <mx:TextArea id="sourceText" x="10" y="36" width="280" height="316"/>
    <mx:TextArea id="destinationText" x="298" y="36" width="280" height="316"/>
    <mx:Label x="10" y="10" text="HTML Text"/>
    <mx:Label x="298" y="363" text="Char Limit"/>
    <mx:Label x="298" y="10" text="Truncated HTML Text"/>
    <mx:Button x="452" y="361" label="Truncate" click="truncateText()"/>
    <mx:NumericStepper id="limitStepper" x="368" y="361" stepSize="10" minimum="100" maximum="1000" value="200"/>
   
</mx:Application>

- Mister

  • Facebook
  • Twitter
  • Google Bookmarks
  • RSS
This entry was posted in AIR, Flex, Flex 3 and tagged , , , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

6 Comments

  1. Joe Dassler
    Posted August 12, 2009 at 5:26 am | Permalink

    Hi Mr thank you mister,
    I am very new to flex.I find there are so many complex tutorials on the web for flex 3.I just wanted to know how to add an html file or a text file from an asset folder to the text area component in flex.i can’t find any.Because i want to be able to edit this file outside of the application….your help will be greatly appreciated….
    Thanks.

  2. Posted August 13, 2009 at 10:06 am | Permalink

    With AIR you just load the file from the local directory just like you load an XML file. From Flex in a browser you need a URL to load the file and again, load it like XML but with text format instead of e4x.

  3. Posted November 3, 2009 at 7:30 am | Permalink

    real usefull and easy to use
    thx for this mister!

  4. David
    Posted January 15, 2010 at 3:47 pm | Permalink

    Hi,

    That was cool! Am actually looking for Java code to truncate HTML. Can you publish that if you still have it?

    Thanks

  5. Posted January 18, 2010 at 4:20 pm | Permalink

    The Java code was created by my spouse, my own persona in-house Java developer. I don’t believe we saved the code as she only created the example for me to convert to Flex. You can change the current class back to Java easily enough though.

  6. Nikolaj
    Posted June 7, 2010 at 8:54 am | Permalink

    Very cool script you made there!
    I’m trying to implement it to a TextField that can truncate given a max width and height, and it works almost perfectly.

    I do seem to have problems with a text that start with a paragraph tag and ends with one. I get returned at the result.length == 0.

    Is it a special case or a natural limitation in the script?

    Thanks for the great code though!

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use [as]...[/as] to post ActionScript code in your comments. Example code in comment: [as] public var myvar:String = "Hello"; [/as]