Using internet explorer I would like to get the position where a person has clicked on text. An error of 3 to 4 characters is fine. The text is not editable and is usually in a span element.
I am aware I could set up a click event listener for the HTMLDocument however I do not always have the HTMLDocument object and thus may miss the event.
I have tried getting a IHTMLSelectionObject, then creating a text range with the IHTMLTxtRange, however when the web page is simply clicked as opposed to at least 1 character being selected then the IHTMLTxtRange has a parent of the HTMLBody and not of the element that was clicked.
The HTMLDocument.activeElement is also unreliable. In my tests it never actually returns the element clicked, it usually returns a major parent of the element somewhere up the tree.
Using MSHTML is there another way to achieve this?
I have also tried using the WIN API GetCursorPos however I do not know what to do with this position, I do not know how to convert this into the actual element.
EDIT: I also thought of an interesting idea. When I need to know the element that has the cursor, I set a mouseDown or click event on the whole document. Then fire my own click and catch the event. In the IHTMLEventObj of the event is a FromElement which I had hoped would tell me where the cursor was. It seems it is always nothing for mouseDown and click events. For me at least this object is only used in for example mouseover events.
The following is what I have when at least a character is selected.
Private Function GetHTMLSelection(ByVal aDoc As IHTMLDocument2, ByRef htmlText As String) As Integer
Dim sel As IHTMLSelectionObject = Nothing
Dim selectionRange As IHTMLTxtRange = Nothing
Dim rangeParent As IHTMLElement4 = Nothing
Dim duplicateRange As IHTMLTxtRange = Nothing
Dim i As Integer
Dim x As Integer
Dim found As Boolean
Try
'get a selection
sel = TryCast(aDoc.selection, IHTMLSelectionObject)
If sel Is Nothing Then
Return -1
End If
'the range of the selection.
selectionRange = TryCast(sel.createRange, IHTMLTxtRange)
If selectionRange Is Nothing Then
Return -1
End If
'the the parent element of the range.
rangeParent = TryCast(selectionRange.parentElement, IHTMLElement4)
'duplicate our range so we can manipulate it.
duplicateRange = TryCast(selectionRange.duplicate, IHTMLTxtRange)
'make the dulicate range the whole element text.
duplicateRange.moveToElementText(rangeParent)
'get the length of the whole text
i = duplicateRange.text.Length
For x = 1 To i
duplicateRange.moveStart("character", 1)
If duplicateRange.compareEndPoints("StartToStart", selectionRange) = 0 Then
found = True
Exit For
End If
Next
If found Then
Debug.Print("Position is: " + x.ToString)
htmlText = duplicateRange.text
Return x
Else
Return -1
End If
Catch ex As Exception
Return -1
Finally
End Try
End Function
I cannot post answer with a nice function that shows how to do this but I will explain the important parts.
Once you have this you now know the point and element that was clicked on.
Private Function getElementTextPosition() As Boolean
Dim sel As IHTMLSelectionObject = Nothing
Dim selectionRange As IHTMLTxtRange = Nothing
Dim duplicateRange As IHTMLTxtRange = Nothing
Dim i As Integer = 0
Dim found As Boolean
Dim x As Integer
Try
'elementWithCursor is a IHTMLElement class variable
If elementWithCursor IsNot Nothing Then
ReleaseComObject(elementWithCursor)
elementWithCursor = Nothing
End If
'docWithCursor is also a IHTMLDocument2 class variable
'cursorPointInDoc is the point relative to the actual document
elementWithCursor = TryCast(docWithCursor.elementFromPoint(cursorPointInDoc.X, cursorPointInDoc.Y), IHTMLElement)
If elementWithCursor Is Nothing Then
Return False
End If
'get a selection
sel = TryCast(docWithCursor.selection, IHTMLSelectionObject)
If sel Is Nothing Then
Return False
End If
selectionRange = TryCast(sel.createRange, IHTMLTxtRange)
If selectionRange Is Nothing Then
Return False
End If
'First check if We have selection text so we will use that as the selected text
'_SelectedText relates to a class property
If selectionRange.text IsNot Nothing Then
_SelectedText = selectionRange.text
selectionRange.collapse(True)
Else
'the the parent element of the range.
selectionRange.moveToPoint(cursorPointInDoc.X, cursorPointInDoc.Y)
End If
'duplicate our range so we can manipulate it.
duplicateRange = TryCast(selectionRange.duplicate, IHTMLTxtRange)
'make the dulicate range the whole element text.
duplicateRange.moveToElementText(elementWithCursor)
'get the length of the whole text
i = duplicateRange.text.Length
For x = 0 To i
If duplicateRange.compareEndPoints("StartToStart", selectionRange) = 0 Then
found = True
Exit For
End If
duplicateRange.moveStart("character", 1)
Next
If found Then
'_CursorPositionInText is a class property and relates to the position where the person clicked in the html text.
_CursorPositionInText = x
_HTMLElementText = elementWithCursor.innerText
Return True
Else
Return False
End If
Catch ex As Exception
Return False
End Try
End Function