[Blink/Webkit] Span elements created while joining adjacent elements
— at Version 5
It's a follow-up of #9998, which covers a single case.
Example 1
- Set data
<p>foo</p>
<p>^bar</p>
- Backspace.
<p>foo<span style="line-height:1.6">bar</span></p>
- Expected:
<p>foo^bar</p>
Example 2
- Set data
<h1>foo</h1>
<p>^bar</p>
- Backspace.
<h1>foo<span style="font-size:13px; line-height:1.6">bar</span></h1>
- Expected:
<h1>foo^bar</h1>
Algorithm for collapsed selection
- Let's consider sample HTML and selection
<div><p><em>x</em></p></div>
<blockquote><p><strong>^y</strong></p></blockquote>
- BACKSPACE or DEL is pressed. We expect the contents of
<blockquote><p>...</p></blockquote>
to be merged with contents of <div><p>...</p></div>
.
- Custom listener in editable listens on DEL and BACKSPACE. The listener is of lowest possible priority to not to interfere with existing code, which is supposed to deal with lists, non–editables, etc.
- Retrieve
range
from selection.
- Abort if
range
is not collapsed (then enter a different branch of the algorithm).
- Get
elementPath
out of the range
(range.startPath()
).
- Create
walker
, starting in path.block
(<p><strong></p>
). Starting from path.block
solves the problem of bogus BR. In case of BACKSPACE, walker is supposed to walk back; in case of DEL – forward.
walker
determines if current case (DOM+selection) should be handled by the custom listener. It traverses DOM tree in search for blocks (when entering elements) and checks if current selection is anchored at the block boundary (when leaving elements).
- Abort, if no previous (BACKSPACE) or next (DEL) block is found or reached boundary of editable. Also abort if current selection is not at the block boundary.
- If previous (BACKSPACE –
<div><p></div>
) or next (DEL – none) block is found, a new range is created and moved to the closest editable position, according to the direction (back or forward with range.moveToClosestEditablePosition()
– <p><em>^</p>
for BACKSPACE). This trick let us move from <div><p></div>
up to <p><em></p>
without hard, vertical traversing.
- Create new
elementPath
for the range once moved to the closest editable position. The block of this elementPath
is our target place for elements to be moved.
- Create intrusive bookmarks (
<strong>||y</strong>
).
- Move all children from the block found in 6. to the one found in 10 (BACKSPACE:
<strong>y</strong>
joins <em>x</em>
).
- Use
element.mergeSiblings
to clean up mess, which could emerge if, i.e.instead of <em>x</em>
there was <strong>x</strong>
so <strong>x</strong><strong>y</strong>
is merged <strong>xy</strong>
.
- Remove block found in 6.
- If block found in 6. (
<p><was strong></p>
) was the only meaningful element in DOM branch, cut it off.
- Restore bookmark saved in 11.
<div><p><em>x</em><strong>^y</strong></p></div>
cc