at the moment I have some troubles writing an abstraction layer for Mozilla and Webkit based browsers for using the DOM-range object (getting and processing user selections).
I have also tried to have a look at frameworks like Rangy but this seems far to complex for my task (I have no idea where exactly in the code to find the information I need. If someone could give me a hint, I would be grateful!).
What I want is simply this:
- get back the reference to the text node the selection starts in and its offset
- get back the reference to the text node the selection ends in and its offset
So far my layer looks like this:
var SEL_ABSTR = {
get_selection: function(window_object) {
return window_object.getSelection();
},
get_range: function(selection) {
return (selection.getRangeAt) ? selection.getRangeAt(0) : selection.createRange();
},
get_range_info: function(range, div_ele) {
var first_node, start_offset;
var last_node, end_offset;
if (range.startContainer == div_ele) {
// selections affects the containing div
first_node = div_ele.childNodes[0];
last_node = first_node;
start_offset = 0;
end_offset = first_node.nodeValue.length;
} else if (range.startOffset == range.startContainer.nodeValue.length && range.endOffset == 0) {
// known bug in Firefox
alert('firefox bug');
first_node = range.startContainer.nextSibling.childNodes[0];
last_node = first_node;
start_offset = 0;
end_offset = first_node.nodeValue.length;
} else {
first_node = range.startContainer;
last_node = range.endContainer;
start_offset = range.startOffset;
end_offset = range.endOffset;
}
return {
first_node: first_node,
start_offset: start_offset,
last_node: last_node,
end_offset: end_offset,
orig_diff: end_offset - start_offset
};
},
};
I have identified two Mozilla bugs for now:
Sometimes when the whole (if its the only one) text node is selected within the containing div I get back a reference to this div instead of a reference to the text node. Now I can handle it and give back a reference to the child of the div which is the text node
Sometimes I get back a reference to the previous sibling with offset == prevSibling.length and and a reference to nextSibling with offset == 0. But the correct reference would be in the middle. I can also handle this.
So what more is there for Mozilla? Webkit works fine!
One should assume that the DOM-range object is standard (and I am not talking of IE, this is another task ...)
greets!
more specific details here:
It was't meant as a critique on Rangy. So I am sorry if it sounded like that. I can imagine that handling these different APIs is not easy per se.
You are right, I wasn't specific regarding the task I am trying to fulfill. My structure is rather simple: I have a div (with attribute contenteditable=true) and text within that div (one text node at the beginning).
Starting from this structure, it is now possible to select text with the mouse and add a property to it; this property is then expressed by a span embracing the selected text and a class assigned to that span representing the selected property. Now it is possible to select again some text and a property. If it is the same text and another property, another class will be assigned to that span or removed if the property already exists. If text is selected which embraces several spans, they will be split in order to express that structure (perhaps you remember my post of July).
<div contenteditable="true">
hello I am
<span class="red">text but I am also <span class="underline">underlined</span></span>
<span class="underline"> also without color</span>
</div>
The algorithm works fine now for "symmetrical" cases: I can build a complex structure and then undo it backwards. It works fine for Safari and Chrome. Now I have of course to develop the algorithm further.
But for now I have problems with Mozilla because I do not understand the system for DOM range objects: startContainer, endContainer, startOffset, endOffset
In my perception regarding my specific case with a div only containing textnodes and spans I assume:
- that startContainer and endContainer always point to a textnode affected by the mouse selection (there are no empty spans, they always contain either other spans or a textnode), marking the beginning and the end of the whole selection
- that startOffset and endOffset indicate the position of the selection within the textnode at the beginning and at the end
In the posted code above I have identified two cases in which Mozilla acts differently from webkit.
So if I knew the rules of Mozilla DOM-range I could inegrate that in my layer so that the behaviour would be the same for webkit and Mozilla.
Thank you very much for your answer.
There is no rule that says selection boundaries must be expressed in terms of text nodes. Consider a selection inside an element that contains only
<img>
elements, for example. So, what you're calling bugs in Mozilla are not bugs at all; in fact, WebKit's selection handling is much buggier than Mozilla's. However, your observation that browsers vary in precisely where they consider a selection boundary to lie when it is at the end of a text node is valid and does complicate things. The best way to deal with it really depends on what you're trying to do, which isn't clear from your question.If you want selection boundaries purely in terms of character offsets within the text content of an element, you can do this (although I'd generally recommend against it for reasons laid out in the linked answer).
Finally, as author of Rangy, I'd like to point out that it's based on the same APIs (DOM Range and Selection) that browsers implement, so I'd say it's no more or less complicated than those APIs. References:
You are noticing a bug where Gecko/Firefox and Presto/Opera incorrectly select the node in which the mouse cursor clicked though did not select. Wait, what? What happens is that if the click registers less than HALF of a character (though greater than 0 pixels) it is not VISUALLY selected HOWEVER Firefox and Opera still select the node itself! Trident/IE and WebKit (Chrome/Safari) do not do this.
I have been battling against this bug for some time, filed a bug at Mozilla (can't remember if I did so with Opera since this was last year) and today finally wrote directly to the editors of the DOM4 specification asking them to implement an EXPLICIT clarification for browser vendors on how to define the
startContainer
.It IS possible to adapt the code to handle this however I do not have the time to write out all the code for you.
Here is the list of methods we'll use...
It's VERY important to remember that the anchorNode is not EXPLICITLY the left starting position though the INITIAL CLICK. If the user click on the right side of text and drags the mouse to the left then the anchor ends up on the RIGHT side of the range. If the user click on the left side of text and then drags the mouse to the right the anchor is then on the left side of the range.
Essentially what you can try to do is see if neither the anchorNode nor the
focusNode
EXPLICITLY match thestartContainer
.Even if you don't need the
startContainer
in ALL situations you're still going to eventually reference it SO it's best to use an object to represent thestartContainer
be it if the browser gets it right the first time (Trident/WebKit) or you have to correct it (Gecko/Presto).This is where it gets a bit tricky especially because different people will have different goals and approaches so I will try to keep the following as generic as possible.
Either you can determine the correct
startContainer
usinganchorNode
orfocusNode
methods OR you can use object detection and W3C compliant methods. Those other methods include....When dealing with style elements such as
s
(strike),strong
,em
(emphasis) and so on you may access the textNode using thefirstChild
unless you have multiple style elements enclosed around the text.If you're having difficulty with determining what methods are available at what parts I recommending using the
in
operator. In example......keep in mind that if you're inside of a loop that it may repeat the options in your textarea element so CTRL+f for the first method and erase from it's second instance down to retain only relevant methods.
Remember to use alert and I often use multiple lines to show multiple pieces of information simultaneously to help me determine what I have. In example...