问题
I have a contenteditable DIV which is linked/synced-back to a textarea.
The contenteditable DIV is a free-for-all sandbox which will create formatting elements etc as they are being invoked. However this does result often in messy stacked elements.
I would like to be able to clean up the code before the textarea form is sent to the server.
It is possible to end up with something like the following:
<div>
<b>
<i>
Hel
</i>
<i>
l
</i>
</b>
<i>
<b>
o World!
</b>
</i>
</div>
Which would ideally be converted to:
<div>
<b>
<i>
Hello World!
</i>
</b>
</div>
If I walked (recursively) through the childNodes of the div I could presumably keep track of the formats (tagName.toUpperCase() == {'B','I' ....} )
// or do a document.queryCommandState
during which I could do a document.execCommand('removeFormat',false,null)
on the selectNode(thenode)
.
However, I'm a bit lost on how I might keep track across neighbouring nodes of the formats.
As reference here is what I recently did for DOM parsing to remove formatting from IMG tags: http://jsfiddle.net/tjzGg/
NB: This is a similar question> jquery - consolidate stacked DOM elements but it is about consolidating useCSS
style lines into one main style. The reason this is a different question is that I am looking to consolidate text with a common style but artificially split over multiple elements because of how the text was formatted. If you take a contenteditable div and individually bold one character at a time you will end up with a single character per element.
回答1:
I have a couple solutions which have their pros and cons.
First, I discovered while playing around in gmail that a contenteditable DIV will 'absorb' a neighbouring node provided the formatting style is in the same current selected mode. This 'freebie' allowed me to merely attempt to reorganize the ordering in which formatting occurs to clean up the majority of the html soup. This is not a complete solution. The ideal solution would be to have the largest formatting mode as the parent with the subset texts would have in decreasing magnitudes be the further nested modes.
In my above artificial example the result would inherently be converted into:
<div>
<b>
<i>
Hel
l
</i>
</b>
....
caveat: This was tested with text only, no images. I would imagine there is a bug or two when dealing with due to the node parsing in solution 1 and use of textContent.length in solution 2.
Solution 1:
The first works in Chrome, but in Firefox invoking execCommand
will cause the node selection to lose focus and become unselected. This is a fatal flaw that I cannot seem to understand or program around. This has been abandoned unless I can figure out how to re-highlight/select the newly formatted node.
http://jsfiddle.net/tjzGg/3/
I would love to be able to get this one working with Firefox. Any suggestions on where I'm going wrong here.
Solution 2:
The second approach is to try come up with a solution for Firefox losing focus. The only way I could handle that is to ignore selecting whole nodes but instead select ONE character at a time, look at its formatting, nuke and reapply in a certain order. This works in both browsers but the DOM is then split into a childNode
for each character. I'm not sure of the best way to combine them (textContent?).
http://jsfiddle.net/LDVpD/3/
[background: looked at jsbeautifier, htmlsoup, html tidy, nokogiri, hpricot, jtidy ..... I'm really surprised that there is no solution on this already. GMail will generate 'ugly' formatting as well!]
I know there are better solutions - I'd love to hear some suggestions.
Update
After testing, it is obvious that solution 2 is ridiculously slow (it would not be complicated to optimize it by keeping track of the head as it is a progressive 'flood' but still it is quite slow) and even one could easily modify it to process entire textNodes but solution 1 seems like a better approach if it only worked in Firefox.
Solution 1+2=3:
I found out that if I applied the formatting as a means to toggle it would work however as predicted the text nodes would grow/shrink based on natural consolidation of neighbouring matched formatting. So it dawned on me while sleeping that if I created a list of text nodes, and went from back to front I could care less if internally the DOM (for Firefox!!!) was growing/shrinking while formatting was being applied. Combining Solution 2's textNode list (and then popping off the tail nodes) this works great. In fact iterating instead of recursing the text nodes (original Solution 1 method) is even faster.
http://jsfiddle.net/tjzGg/4/
NB: The selectNodeContents vs selectNode
来源:https://stackoverflow.com/questions/16226671/consolidate-stacked-dom-formatting-elements-contenteditable-div