Table of contents
- Find Cursor Position in a HtmlText Object (RichTextEditor, TextArea, TextField)
- Find Cursor Position in a HtmlText Object (RichTextEditor, TextArea, TextField) – UPDATE
Last task I finished was something linked with templates. I needed to create a simple template engine where adding a field was a necessity. I started using RichTextEditor which is based on a TextArea so it implements htmlText property. That is an easy way having all that nice functionalities like bold, italic, font choosing, color chooser, alignment and others, in your application.
What I needed is to insert some tags at a specific point in the text. The problem I run into was that RTE (RichTextEditor) shows you the position of the cursor in the text but not in the htmlText which is not a bug.
For example… I had the text bellow:
<TEXTFORMAT LEADING="2"><P ALIGN="LEFT">
<FONT SIZE="13" COLOR="#555555" LETTERSPACING="0" KERNING="0">
asd asd {asd asd asdasd <U>asd</U> mmm
</FONT></P></TEXTFORMAT>which has HTML tags in it as is rendered as this:
asd asd {asd asd asdasd asd mmm
When positioning the cursor after the first group “asd” the normal the selection.beginIndex property of the RichTextEditor displayed the position in the normal text, which is three, but in my case I needed the position in the HTML string (htmlText) to be able to insert there my tag.
I asked some of my flex colleagues and we found a way of counting the position in the htmlText from the position in the normal text. The way is parsing char by char the htmlText string and where the < char was found we set a flag which will enable us to know when we are inside a tag this way not counting that chars. You can see everything at the end of this post in the calculateHtmlPosition function.
Important thing to mention (example)…
htmlText:
<u>asd</u> asd
text:
asd asd
When the cursor is before “asd” in the RTE the position in the htmlText is 0 – that means that the cursor is positioned before the <u> opening tag. But… when the cursor is positioned after “asd” the position in the htmlText excludes the closing </u> tag. If the cursor is after the space and just before the second “asd” the position is including both closing tag and the space – so 5 more characters. This how it should work. You can see this in many other applications like Microsoft Word where, if you are starting to type immediately after an underlined block of text, your new inserted text is also underlined.
I hope you understood this article. There may be other ways to achieve this but this is my way of resolving it and if you found one of those other ways please tell us. We are willing to learn how to do it better.
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()"> <mx:Script> <![CDATA[ import mx.containers.HBox; import mx.controls.Alert; import mx.controls.Button; import mx.controls.RichTextEditor; public var RTE:RichTextEditor = new RichTextEditor(); public var RTEText:String = '<TEXTFORMAT LEADING="2"> <P ALIGN="LEFT"> <FONT SIZE="13" COLOR="#555555" LETTERSPACING="0" KERNING="0"> asd asd {asd asd asdasd <U>asd</U> mmm </FONT></P></TEXTFORMAT>'; public function init():void { RTE.htmlText = RTEText; addChild(RTE); var hbox:HBox = new HBox(); hbox.y = 310; var btn:Button = new Button(); btn.label = "HTML"; var btn_pos:Button = new Button(); btn_pos.label = "Pos"; var btn_pos2:Button = new Button(); btn_pos2.label = "Norm Pos"; hbox.addChild(btn); hbox.addChild(btn_pos); hbox.addChild(btn_pos2); addChild(hbox); btn.addEventListener(MouseEvent.CLICK,showHTML); btn_pos.addEventListener(MouseEvent.CLICK,getHtmlPosition); btn_pos2.addEventListener(MouseEvent.CLICK,getPosition); } // This function is OBSOLETE - Please see next article in this Serie public function calculateHtmlPosition(htmlstr:String, pos:int):int { if (pos <= -1) return -1; var htmlPos:int = 0; var isInsideTag:Boolean = false; var cnt:int = 0; for (var i:int = 0; i < htmlstr.length; i++) { if (cnt>=pos) break; var currentChar:String = htmlstr.charAt(i); if (currentChar == "<") isInsideTag = true; if (!isInsideTag) cnt++; if (currentChar ==">") isInsideTag = false; } return i; } public function showHTML(event:MouseEvent):void { Alert.show(RTE.htmlText); } public function getHtmlPosition(event:MouseEvent):void { Alert.show(calculateHtmlPosition(RTE.htmlText, RTE.selection.beginIndex).toString()); } public function getPosition(event:MouseEvent):void { Alert.show(RTE.selection.beginIndex.toString()); } ]]> </mx:Script> </mx:Application>
Tags: ActionScript, htmltext, positioning, RichTextEditor, RTE
This post was written by Andrei Ionescu
Views: 10665










Thanks for the helpful code. Just a note that your calculateHtmlPosition does not take into account encoded characters that take up multiple html characters but only a single displayed character position. For example, & takes five html characters but is displayed as a single &. It was easy to make a minor modification to your code to account for these.
One more thing: The <P> tag needs to count the line return character from the text field. The htmlText converts the line ending character to a <P> tag. When you stop counting characters (while isInsideTag is true) in that tag, you leave out the line ending character.
Yes Rick! You’re right. Can you post your modified function into a comment and I’ll add them right into the article?
What follows is the function I came up with, thanks to the inspiration from your original function. This hasn’t yet been thoroughly tested, but it seems to be working in my application. You’ll notice that I chose to grab the entire tag once currentChar = <. I think it simplifies things some, and makes it much easier to respond to certain tags. Note that this assumes that htmlstr is proper html. I don’t have the error checking in place in case there is no > following the <.
function calculateHtmlPosition(htmlstr:String, pos:int):int {
if (pos < = -1) {
return -1;
}
var htmlPos:int = 0;
var isInsideEscape:Boolean = false;
var cnt:int = 0;
for (var i:int = 0; i < htmlstr.length; i++) {
if (cnt >= pos) {
break;
}
var foundTag:Boolean = false;
var currentChar:String = htmlstr.charAt(i);
if (currentChar == "< ") {
// found the open bracket, now retrieve the rest of the tag
foundTag = true;
var endOfTag:int = htmlstr.indexOf(">",i);
var tag:String = htmlstr.substring(i,endOfTag + 1);
i = endOfTag;
if (tag == "") {
cnt++;
}
}
if (currentChar == "&") {
isInsideEscape = true;
}
if (isInsideEscape) {
if(currentChar == ";") {
isInsideEscape = false;
}
}
if (!isInsideEscape && !foundTag) {
cnt++;
}
}
return i;
}
Sorry, but the code I just posted has the < and > failing to display. If it’s impossible to understand let me know and I can send it to you.
Yes, the code is a bit messed up but we will fix it.
[...] Object (RichTextEditor, TextArea, TextField) – UPDATE26.03.08 | Comment? Table of contentsFind Cursor Position in a HtmlText Object (RichTextEditor, TextArea, TextField)Find Cursor Position in a HtmlText Object (RichTextEditor, TextArea, TextField) – [...]
New article here with the new calculateHtmlPosition function modified to repair the flaws mentioned by Rick in previous comments.
Well, theres much more easier way to do it
no parsing included.
public static function getHtmlCaretPosition( target:UIComponent, caret:int ):int{
var range:TextRange = new TextRange( target, false, 0, caret );
var pattern:RegExp = /(.*?)(< \/([^<>]*>< \/[^<>]*)*>)/gm;
var cleanHtml:String = range.htmlText.replace( pattern, "$1");
var htmlLength:int = cleanHtml.length-1;
return htmlLength;
}
Ive almost used your code, but it seemed a bit too complicated for such an easy task
cheers
Thanks mooska! Great approach… I’ll post you’re comment also in the second part of this series.
Well, site removed a bit from regexp (it should find closed tags) and I havent test it much
as I needed this fast, but the way seems ok.