Html – Displaying HTML node in tree – height issue


Grrr so close yet still failing…

I display this tree, in Flex, which contains two nodes types:

  1. Regular nodes, which are rendered normally like text (because they are!)
  2. Rich (HTML) nodes – that's where things get twisted

Note that my issue is when I dynamically add a new (HTML) node to my tree.

So… How do I display HTML nodes?

  1. I subclass TreeItemRenderer
  2. In that subclass, I override set data() and add a text child to my renderer

Therefore I now have:

[icon] [label]

[text component]


The default label is a pure text component, not HTML-capable, hence the extra component: I want to display the new guy and forget the default label.

  1. (continued) I override updateDisplayList() and, if the node is a rich one, I set label's height to zero, set my component's x and y to label'x and and y.

So…what am I missing? Ah, yes: I need to set my node's height since HTML text can be bigger or smaller than its text counterpart.

  1. (continued) I override measure()
  2. If my node is not a rich one, I simply invoke super.measure() and return
  3. If it is a rich one, I give my html component a width (htmlComponent.width = explicitWidth – super.label.x;) and its height should be automatically computed.

This gives me a fairly reliably unreliable result!

When I fold/unfold my tree, every other time, I seem to get a correct height for my HTML node. The other time I get a height of '4' which happens to be the HTML component's padding alone, without content.

I know that I must be doing something fairly stupid here…but I am not sure what. I will post my code if my rambling is too incoherent to make any sense of…

**** EDIT: here is the source code for my renderer
As you can see, only 'notes' nodes use HTML.
I add a 'htmlComponent' child that will display the rich text while the default label is zero-sized and disappears.
It's definitely very raw code, as it's in progress!

    package com.voilaweb.tfd
    import mx.collections.*;
    import mx.controls.Text;
    import mx.controls.treeClasses.*;
    import mx.core.UITextField;
    import mx.core.UIComponent;
    import flash.text.TextLineMetrics;

    public class OutlinerRenderer extends TreeItemRenderer
        private function get is_note():Boolean
            return ('outlinerNodeNote' == XML(;

        override public function set data(value:Object):void
   = value;
            var htmlComponent:Text = super.getChildByName("htmlComponent") as Text;
                htmlComponent = new Text();
       = "htmlComponent";
                htmlComponent.htmlText = XML('nodeText');
                htmlComponent.htmlText = null;
            setStyle('verticalAlign', 'top');

  * Today we've learnt a valuable lesson: there is no guarantee of when createChildren() will be invoked.
  * Better be dirty and add children in set data()
        override protected function createChildren():void
            var htmlComponent:Text = new Text();
   = "htmlComponent";
        override protected function measure():void
            var htmlComponent:Text = super.getChildByName("htmlComponent") as Text;
            //Setting the width of the description field
            //causes the height calculation to happen
            htmlComponent.width = explicitWidth - super.label.x;
            //We add the measuredHeight to the renderers measured height
            //measuredHeight += (htmlComponent.measuredHeight - label.measuredHeight);
            // Note the silly trick here...hopefully in the future I figure out how to avoid it
            // Here is what happens: we check if measuredHeight is equal to decoration such as margin, insets...rather than that + some height
            // If so, then we need to come up with an actual height which we do by adding textHeight to this height

            // Note that I care about text being equal to margin etc but do not have proper access to these
            // For instance UITextField.TEXT_HEIGHT_PADDING == 4 but is not accessible
            // I am going to check if "<10" that will cover this case...
            trace("For text " + htmlComponent.htmlText);
            trace("width = " + htmlComponent.getExplicitOrMeasuredWidth()+" x height = " + htmlComponent.getExplicitOrMeasuredHeight());
            var m:TextLineMetrics = htmlComponent.measureHTMLText(htmlComponent.htmlText);
            //if(10 > htmlComponent.measuredHeight && !isNaN(htmlComponent.explicitHeight))
            //htmlComponent.explicitHeight = m.height + htmlComponent.measuredHeight;
            //if(htmlComponent.measuredHeight < 10) htmlComponent.explicitHeight = 50;

            //measuredHeight += (htmlComponent.getExplicitOrMeasuredHeight() - super.label.getExplicitOrMeasuredHeight());
            measuredHeight += (htmlComponent.getExplicitOrMeasuredHeight() - label.getExplicitOrMeasuredHeight());
            trace("m:"+m.height+" Height: " + htmlComponent.getExplicitOrMeasuredHeight());

        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
            super.updateDisplayList(unscaledWidth, unscaledHeight);
            label.height = label.getExplicitOrMeasuredHeight(); // If you tell me my height, then I shall use my variable height!


                label.height = 0;
                var htmlComponent:Text = super.getChildByName("htmlComponent") as Text;
                htmlComponent.x = label.x;
                htmlComponent.y = label.y;
                htmlComponent.height = htmlComponent.getExplicitOrMeasuredHeight();

                graphics.drawRect(0, 0, unscaledWidth, unscaledHeight);

            var complete:XMLList = XML('complete');
            if(complete.length() > 0 && true == complete[0])
                var startx:Number = data ? TreeListData(listData).indent : 0;
                    startx += disclosureIcon.measuredWidth;
                    startx += icon.measuredWidth;
                graphics.lineStyle(3, getStyle("color"));
                var y:Number = label.y + label.getExplicitOrMeasuredHeight() / 2;
                graphics.moveTo(startx, y);
                graphics.lineTo(startx + label.getExplicitOrMeasuredWidth(), y);

Best Solution

You made false assumption about label component in default renderer - it is capable of displaying html content. This renderer works for me:

public class HtmlTreeItemRenderer extends TreeItemRenderer {
    override protected function commitProperties():void {
        label.htmlText = data ? listData.label : "";