You probably never asked yourself about it, but how complicated is
the deletion of the selection in a markup-based Wysiwyg editor? In one
single word: complicated.... I will try below to make you understand
what a Wysiwyg markup-based editor does when you hit the delete key.
Let's take a simple example :
<h1>aa[aa</h1>
<ul>
<li>bbbb
<ul>
<li>cc]cc</li>
<li>dddd</li>
</ul>
</li>
<li>eeee</li>
</ul>
The selection is everything between the square brackets (included).
Now the user hits the delete key. The natural algo for that is the
following one (I'm not saying it's the only one; see
DOMRange.extractContents to see another one):
- find all the "visible" nodes in the various ranges (here we have
only one range) of the selection. A "visible" node is a text node or an
empty element.
- split the start and end nodes of each range of the selection if
necessary, for instance if these nodes are text nodes and the selection
splits the nodes in two nodes of non-zero length.
- for each visible node in each range of the selection, remove the
node and remove recursively all the parents if they are "removable". A
"removable" parent is an empty element or, depending on its tag name,
an element containing only text nodes made of whitespaces (/s in
regexps); some elements are explicitely not "removable", body for
instance.
This method is enough for the test case above and here's the
expected result:
<h1>aa</h1>
<ul>
<li>
<ul>
<li>cc</li>
<li>dddd</li>
</ul>
</li>
<li>eeee</li>
</ul>
But now imagine we have some markup that requires merging after the
deletion of the selection. Here's one:
<ul>
<li>aaaa</li>
<li>bb[bb</li>
</ul>
<ul>
<li>cc]cc</li>
<li>dddd</li>
</ul>
What the user really expects here when the selection is deleted is
the merging of the two ULs into one single list. We need then to
improve a bit our algo above...
- find all the "visible" nodes in the various ranges (here we have
only one range) of the selection. A "visible" node is a text node or an
empty element.
- split the start and end nodes of each range of the selection if
necessary, for instance if these nodes are text nodes and the selection
splits the nodes in two nodes of non-zero length.
- traverse all ranges in our selection and preserve a list of all
nodes traversed with the traversal direction (start, up, down, next)
Let's have an example here, and let's reuse the first example at the
top of this article wih a h1 followed by a ul. The list of traversed
nodes is then:
node
|
direction
|
"aa"
|
start
|
h1
|
up
|
ul
|
next
|
li
|
down
|
"bbbb"
|
down
|
ul
|
next
|
li
|
down
|
"cc"
|
down
|
For our examples of two adjacent lists, the list of traversed nodes
is:
node
|
direction
|
"bb"
|
start
|
li
|
up
|
ul
|
up
|
ul
|
next
|
li
|
down
|
"cc"
|
down
|
But that's not all... There can be non-significant text nodes in the
DOM between elements, like a carriage return between the two ULs. So to
make sure we correctly merge during our deletion, the third step of our
algo should be this one:
- for each node being an element and having a direction "next" in
the list of traversed nodes above, find the previous "visible" sibling,
discarding text nodes containing only white spaces if the element is a
block. If the previous visible sibling is also an element and of same
type than the reference node, and if these elements are "mergeable",
then append all the children of the reference node to the previous
visible node's children and delete the reference node. Two "mergeable"
elements are for instance two paragraphs, or two h1 elements, or two dl
elements; two inline text elements like span or strong are also
mergable if
The rest of the algo is similar to what we did for the h1/ul example
above:
- for each visible node in each range of the selection, remove the
node and remove recursively all the parents if they are "removable". A
"removable"
parent is an empty element or, depending on its tag name, an element
containing only text nodes made of whitespaces (/s in regexps); some
elements are explicitelynot "removable", body for instance.
And then result is:
<ul>
<li>aaaa</li>
<li>bb</li>
<li>cc</li>
<li>dddd</li>
</ul>
Next time, we'll dive into some high-level functions like indentations or list creation.