Warning:
This wiki has been archived and is now read-only.

ContentEditable

From HTML Editing APIs
Jump to: navigation, search

from http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2009-December/024627.html

Dealing with inconsistencies in browser behavior during typing is one of the hardest parts of writing a sane web-based rich-text editor. We have researched what a variety of text editors (Word, Powerpoint, Wordpad, TextEdit) and browsers using contentEditable/DesignMode currently do when you press different keys on the keyboard, in a number of rich text situations, with a variety of text selections. From this data, we attempt to identify recommendations that are OS-agnostic for editing behaviors inside a contentEditable element.

Since we've used a bunch of tables and formatting, we've attached the doc as an HTML file in addition to inline.

Ojan

Julie Parent, Ojan Vafai, Alex Russel, Eric Seidel

About this document

We have researched what a variety of text editors (Word, Powerpoint, Wordpad, TextEdit) and browsers using contentEditable/DesignMode currently do when you press different keys on the keyboard, in a number of rich text situations, with a variety of text selections. From this data, we have identified a set of common behaviors, listed below in the terminology section. Finally, we have formed recommendations for default behaviors in these circumstances and, when necessary, added additional execCommands or specification details to existing execCommands.The execCommands match the behavior of the user-initiated action in order to give developers control over these behaviors when the defaults are incorrect for a specific use case.


We tried to avoid defining any behaviors that are OS-specific and to make the descriptions as clear as possible, but there's a lot of detail here that could use the scrutiny of more eyes. We also tried to make the text as clear as possible, but are not spec writers and don't intend this to be the actual text used in the spec. Please comment on any text that doesn't make sense.

Terminology

Typeable Position: A normalized position (node, offset), for which inserting a character produces valid html. Arrow keys move by typeable positions. Basically, anywhere you can put a character and it isn't an HTML5 error.

Typing style: Internal state of formatting that should be applied when text is inserted. The typing style is automatically emptied when the user modifies the selection (e.g. moves the caret). This is used for things like execCommand('bold') on a collapsed selection.

Caret: Collapsed selection.

Block context: The type of block to use when adding a block during a split.


Legend: Common behaviors

See [#Appendix Appendix] for details on differences in the current implementations of these behaviors.


Short name Description
Undo: add entry Adds a new entry to the undo stack
Undo: combine with text Treat like any other text insertion - UA can group together to form a single entry.
focus next Contents: nop.Undo: nop.Selection: Focus moved to next tabable item.
focus prev Like focus next, but moves to the previous item in tab order
focus next cell Contents: nop.undo: nop.Selection: Focus is moved to the next cell (either next cell in a row, or to the first cell in the next row) Cursor is at start of next cell or cell contents are selected. See below for current details
select(item) Cause the user visible selection to fully select the item.
indent paragraph(s) Indent each paragraph individually.Undo: add entrySelection: unchanged
outdent pragraph(s) If indented, outdent one level.undo: add entryIf not indented, nop.Selection: unchanged
indent li Contents: See table below for current implementation differences.Undo: add entry.Selection: Cursor is unchanged visibly.
indent list Contents: Html is no longer list based (this is word only).Undo: add entrySelection: Cursor is unchanged visbily.
insert tab char Contents: Inserts tab character at typeable caret location.Undo: combined with text.Selection: Caret is after tab char.
insert spaces Contents: See table below for different implementations of inserting spaces.Undo: combine with text.Selection: Cursor is after the spaces and any tags they added.
insert table row Contents: Adds a new table rowUndo: add entrySelection: Cursor in first cell of new row
break out breaks out of the current node and adds a new node of the type of the new context it's in.Note: Anytime a node is split, all copyable properties end up on both nodes (as an example, ID is not copyable since that would leave you with an invalid DOM).
split splits the current block node at the cursor
split contents split contents into two nodes of the block context and make them the child of the parent
br inserts a br
break out and wrap in block breaks out of the current node and wraps both new nodes in a block of the type of the new block context
split wrap in block split inlines and wrap in two elements of the block context type
replace selected Text selected is removed from the document and replaced with new content.Cursor is collapsed at end of the newly inserted text.Undo: Either combine with text or add entry. UA specific.
remove(item) Removes the item from the dom.If the item removed would create an empty block tag, the UA should insert a non-breaking space that the user is unable to put the cursor after and is automatically deleted when the user types (this is roughly IE's behavior).If the item removed would create an empty inline styling tag, the tag and it's formatting should be removed and entered into the typing style.For backspace: Cursor moves to typeable position before the removed item.For delete: Cursor moves to typeable poistion after the removed item.
maintain styling Removes an inline styling tag from the dom and adds it to the typing style.Aka, given "a<b>foo</b>c", if you start at the end of "foo", and backspace 3 times, you are left with "ac", but if you insert a "b" before changing the selection, you'd get "a<b>b</b>c".
merge Merge 2 blocks into 1. The second block (in document order) is merged into the first. The type and styling of the second block are lost.Aka, given <p>paragraph</p><h1>header</h1>, with the cursor at the start of the H1 and pressing backspace results in <p>paragraphheader</p> and the <h1> is lost, and the cursor remains at the merge point, between "paragraph" and "header". For delete, if the cursor is at the end of the P, the result is the same as backspace at the start of the H1.
nop Nothing happens to contents, selection, undo stack, etc.
delete(rangeContents) Delete all fully selected nodes and all selected characters, but preserve partially selected nodes. Cursor is collapsed after this action.
no merge Adjacent blocks are not merged into one block.
merge + wrap styling Merge adjacent blocks, but wrap the second blocks's contents in a styled span that keeps the original styling and cancels out any styling from the first block.
cursor: end of start Cursor is located after the last character of the start node, but before the end node. That is, if you type, the inserted character will go into the start node.Note: in all contexts, selection direction does not effect this.
cursor: collapse(toStart) Collapse the cursor to the start/end.
backwards merge Merge 2 blocks, but rather than merging second block into first block, merge first block into second block.
no content change Text contents do not change. Not a total nop since selection can still change.

Key to the tables below

Green: Current behavior matches the recommended behavior.

Red: Current behavior does not match the recommended behavior.

Orange: Current behavior nearly matches the recommended behavior. Difference is usually a platform specific subtlety.

N/A: Behavior does not apply in this context.


Collapsed selections (carets)

Text insertion

[#FOOTNOTE-1 1]

Alphanumerics

Recomendation for HTML 5:

  1. Normalize the current caret to a typeable Position.
  2. Insert the character at the typeable position.
  3. Adjust the typeable position to be collapsed after the inserted text.

execCommand('insertText', false, singleCharacter) should be equivalent to pressing the alphanumeric key on the keyboard. Currently supported by WebKit.

Notes: Current and recommended behaviors match here, and there are no known differences been UA's, other than with undo group formation, white-space rebalancing, and where typeable positions are. Specifically:

  • Undo: combine with text (UA specific when to start new groups).
  • Need to deal with white-space rebalancing to enable multiple spaces.
  • Given a dom with multiple dom positions for the same visible positions, UA's currently vary in which position is considered the typeable position. Example: given foo<b>bar</b>baz:
    • With the cursor between bar and baz (in the way a user can create, not programatically), insertions happen:
      • IE: if you click into the location, text goes into the B tag. If you use arrow keys, it goes outside.
      • FF: If you click in, outside. If you arrow from outside the tag, outside. If you arrow from inside the tag, inside.
      • Chrome: always in.
      • Word: always in.
      • PPT: always in.
    • With the cursor between foo and bar, insertions happen:
      • IE: Click, outside B. Arrows, inside.
      • FF: Click, outside. Arrow from inside the tag, inside. Arrow from outside the tag, outside.
      • Chrome: Always out.
      • Word: always out.
      • PPT: always out.

Paste

Recomendation for HTML 5:

  1. Normalize the current caret to a typeable Position.
  2. Begin a new undo group
  3. Insert the html, performing cleanups to create valid html.
  4. End undo group
  5. Adjust the typeable position to be collapsed after the inserted text.

execCommand('paste', false, null) should be equivalent to paste from other sources (edit menu, right click, control + v).


Notes:

  • Current and recommended behaviors match here, except for cleanups required to create valid html on insert.
  • What if the html to insert would be invalid at that given location? Currently, WebKit and IE do no (?) cleanup, and FF does some cleanup to create valid html.
  • Example: Pasting a block into an inline: HTML is "<b>fo|o</b>", where | is the cursor. Paste "<h1>bar</h1>".
    • WebKit: <b>fo<span><h1>bar</h1></span>o</b> (INVALID)
    • IE 7: <b>fo<h1>bar</h1>o</b> (INVALID)
    • FF 3: <b>fo</b><h1>bar</h1><b>o</b> (VALID)

Text deletion

#FOOTNOTE-2 2 Notes:

  • Undo behavior for deletions matches insertions, that is, multiple insertions|deletions can combine into a single entry.
  • In PPT, FF, insertions and deletions do not share an undo group, in Word, IE, Chrome they do.

Backspace

Recommendation for HTML 5:

If cursor at the start of a block, merge with previous block. Otherwise, remove previous item. The table below shows how closely current editors match this recommendation.


execCommand('delete', false, null) should be equivalent to backspace.


Current Default Behaviors and Recommendations:


ContextPrevious item is: PPT Word IE FF Chrome Textedit Recomendation
text remove(char) remove(char) remove(char) remove(char) remove(char) remove(char) remove(char)
beginning of P/H/other blocks merge merge merge merge merge merge merge
image N/A - PPT doesn't allow images in text select(image)double backspace: remove(image) remove(image) remove(image) remove(image) remove(image) remove(image)
link unlinks unlinks unlinks remove(char), subsequent insert goes inside link remove(char), subsequent insert goes out of link remove(char),subsequent insert goes inside link remove(char), subsequent insert goes into link (if it still exists)[Informal user study: If the link text and href match, and were auto-linked to begin with, then this should also unlink.]
table N/A - PPT doesn't allow tables in text nop remove(table) remove(last char in last table cell) merge into last table cell merge into last table cell select(table)[Informal user study: Many users concerned here about an entire table just being deleted by a single backspace action]
single char of styled text remove(char), maintain styling remove(char), maintain styling remove(char), maintain styling remove(char), maintain styling remove(char), maintain styling remove(char), maintain styling remove(char), maintain styling
table cell remove(char)cannot step out of cell remove(char)cannot step out of cell remove(char)cannot step out of cell remove(char)cannot step out of cell if at start of cell, move focus to previous cell (if there is one) or move out of table if in first cellif in first cell of empty row - delete rowNormal:remove(char) remove(char)cannot step out of cell remove(char)cannot step out of cell
list - one char in a li remove(char)li turns into ghost bullet, that will be a bullet if you type into it again but doesn't render as a bulletbackspace in a ghost bullet will remove the bullet remove(char) remove(char) remove(char) remove(char) remove(char) remove(char)
list - before first char remove(li) remove(li) remove(li) + break out merge merge remove(li) + break out remove(li)Note: Second backspace performs the merge.
list merge into last li merge into last li merge into list, but not into an li merge into last li merge into last li merge into last li merge into last li
list - empty li only happens as ghost list, remove(li) remove(li) remove(li) + break out merge merge remove(li) + break out remove(li)

Forward Delete

Recomendation for HTML 5:

A forward delete before an item should be equivalent to a backspace after the item. The table below shows how closely editors match this recommendation. If a row is not included in the table below, then the behavior matches the backspace behavior. For individual cells, (m) = matches the backspace behavior.


execCommand('ForwardDelete') should be equivalent to pressing forward delete key (implemented in Webkit).


Current Default Behaviors and Recommendations:


ContextNext item is: PPT Word IE FF Chrome Textedit Recomendation
link remove(char)subsequent insert out of link select(link)double delete: remove(link) remove(char)subsequent insert out of link remove(char)subsequent insert into link(m) remove(char)subsequent insert out of link(m) remove(char)subsequent insert out of link(m) remove(char).Subsequent typing into link (if it still exists)If link text matches href and auto-linked, also unlink.(m)
table N/A(m) merge previous content into first table cell remove(table)(m) delete first char in first table cell (m) select(table)double delete: remove(table) merge first cell contents into previous content. Table remains with no first cell. select(table)(m)
single char of styled text remove(char) remove(char) remove(char) remove(char)maintain style(m) remove(char)maintain style(m) remove(char)maintain style(m) remove(char)maintain style(m)
table cell remove(char)cannot step out of cell(m) remove(char)cannot step out of cell(m) remove(char)cannot step out of cell(m) remove(char)cannot step out of cell(m) Normal: remove(char)(m)end of cell: nop, unless proceeding empty row, remove(row) Normal: remove(char)end of cell: deletes cell + merges contents from that cell into next cell. if last cell, merges content after table into last table cell remove(char)cannot step out of cell(m)
list - one char in a li enters ghost bullet modeUnlike backspace, further deletes won't remove the ghost remove(char)(m) remove(char)(m) remove(char)(m) remove(char)(m) remove(char)(m) remove(char)(m)
list - after last char in a li merge merge move cursor to start of next li, if there is one, else, merge merge(m) merge(m) move next li and its contents onto this line, so there are two bullets on one line merge
list merge merge delete list, leave contents of lis intact, just not in lis merge merge move first li and its content onto the line with previous content, so there is a bullet halfway through the line merge
list - empty li only happens as ghost list, remove(li)(m) remove(li)(m) leave li alone, move cursor down to next li, if there is one, else merge into list merge(m) merge(m) move next li and its contents onto this line. remove(li)(m)

Cut

For a collapsed selection, this is a noop, it does not modify the undo stack.[#FOOTNOTE-3 3]

Tab Key

[#FOOTNOTE-4 4]

Note: FF 3, IE 7, Chrome 1 always perform focus next for tab, no matter what context. They have been eliminated from the tables to avoid clutter.

General undo philosophy

  1. If the tab action does not change contents, the undo stack is not modified.
  2. If the tab action causes standard text content changes (inserting spaces, spans with spaces, tab character), then the tab action is added to the undo stack just like any other text insertion and can be combined with other text insertion into a single undo entry as the UA sees fit.
  3. If the tab action changes contents, but the contents are more structural change (add table row, indent list item), then this action is added as a single entry to the undo stack.

Recomendation for HTML 5: This is largely context-specific, so recomendations are provided in the far right column of the table below.


execCommand('indent') should be equivalent to tab when listed in the table as "indent li". execCommand('insertText', false, " ") or execCommand('insertText', false, "\t")should be equivalent to tab when listed in the table as "insert tab char/spaces".


Current Default Behaviors and Recommendations:


Context PPT Word WordHTML mode FF2 (Linux) Wordpad TextEdit RTF mode TextEdit Mac mode Recommended default behavior
li, with or without text content, cursor at start of li, inside list (not the first li in the list) indent li indent li indent li indent li insert tab char indent li indent li indent li
li, with or without text content, cursor at start of li, start of list (first li in list) indent li indent list indent list indent li insert tab char indent li indent li indent li
li, with or without content, cursor inside content insert tab char insert tab char insert spaces (8 nbsp + " ") indent li insert tab char insert tab char insert tab char insert tab char/spaces
li, with content, cursor at end of li insert tab char insert tab char insert spaces (8 nbsp + " ") indent li insert tab char insert tab char insert tab char insert tab char/spaces
table cell - not last cell focus next cell focus next cell focus next cell focus next cell focus next cell focus next cell focus next cell focus next cell
table cell - last cell insert table row insert table row insert table row insert table row insert table row move visibly outside of tableif there is a node after table, cursor is thereotherwise, cursor is visibly outside of table, but when you type, goes into last table cell move visibly outside of tableif there is a node after table, cursor is thereotherwise, cursor is visibly outside of table, but when you type, goes into last table cell insert table row
PRE N/A N/A insert spaces (5 nbsp + " ") Insert tab (\t) N/A insert tab converts pre to P, insert spaces insert tab/spaces
CODE N/A N/A insert spaces (3 nbsp + " ") Insert spaces N/A insert tab converts code to P, insert spaces insert tab/spaces
normal rich text text (not plaintext area) insert tab char insert tab char Insert spaces (8 nbsp + " ") Insert spaces insert tab char insert tab char Insert spaces insert tab/spaces
plaintextarea N/A N/A N/A focus next N/A N/A N/A focus next

Enter

[#FOOTNOTE-5 5]

Recommendation for HTML 5:

This is largely context-specific, so recomendations are provided in the far right column of the table below. Undo behaves the same as regular text insertion. (shift|option)+enter: Always inserts a br.


execCommand('SplitBlock', false, blockTagName): Performs the split command. Walks up the tree to find the first parent block and splits all the nodes from the cursor to that block maintaining all attributes on the split nodes, including inline styling. If one of the nodes being split is an anchor tag, the anchor should not be included in the second half. Stops at the first table-cell or contentEditable ancestor. If there is no parent block between the caret and the first table-cell/contentEditable ancestor, then it wraps the inline contents before and after the selection in an element of type blockTagName.


We stop at the first table-cell ancestor because splitting it does not actually perform the user-expected behavior of adding a new block.


If any nodes that are split have an ID, then the ID is not added to the second node after the split.In the table below, this command is listed below as "split", "split contents", "split wrap in block", and "break out and wrap block".


execCommand('SplitOutOfBlock', false, blockTagName): Performs a split command and changes the type of the second block to that of blockTagName.


In both commands above, blockTagName defaults to 'DIV'. This matches the WebKit behavior as opposed to the IE behavior of defaulting to 'P'. DIV makes more sense to users as it matches other word processor behavior of not having default margins. Also, it is semantically more accurate as the UA does not know what the user intends to put inside the element.In the table below, this command maps to "break out".


execCommand('InsertBreak', false, null): Inserts a BR element.


Current Default Behaviors and Recommendations


Context PPT 2008Mac Word 2008 Mac Doc mode Word 2008 Mac HTML mode IE 7 FF 3 - Win Chrome Wordpad textedit Mac RTF mode textedit Mac HTML mode Recommended default behavior
end of header n/a break out break out break out break out and insert two brs break out n/a n/a split and add a bunch of styling break out
beginning/middle of header n/a split split split split split n/a n/a split and add a bunch of styling split
paragraphs split split split split split split split split split split
table cell split contents split contents split contents split contents br br split split contents split contents split contents
div n/a n/a split contents split br split n/a n/a turn everything into p with styling split
li split split split split split split split split split split
empty li split with placeholder list item break out (see footnote) break out (see footnote) break out break out break out break out and add two line breaks break out break out break out
empty li in nested list split with placeholder list item break out break out break out break out break out n/a break out break out break out
inlines maintained maintained spilt wrap in block split wrap in block br br maintained maintained maintained split wrap in block
link split open link open link break out and wrap in block br br n/a split split break out and wrap block
blockquote n/a n/a split contents split contents br split n/a n/a turn everything into p with styling split
pre n/a split split split br split n/a n/a turn everything into p with styling br


Non collapsed selections

Text insertion

[#FOOTNOTE-6 6]

Notes: Undo behavior is the same as collapsed case.

Alphanumerics

Recomendation for HTML 5: Equivalent to backspace + insert (execCommand('delete') + execCommand('insertText')) on collapsed selection.

Paste

Recomendation for HTML 5: Equivalent to backspace + paste (execCommand('delete') + execCommand('insertHTML')) on collapsed selection.

Text deletion

[#FOOTNOTE-7 7]

Backspace

Recomendation for HTML 5:

Always delete the contents of the selected range. Unless a table cell is one of the endpoints, merge into the previous element, but preserve style from the second.


execCommand('delete') with a range selected should be equivalent to backspace with the same range selected.


Current Default Behaviors and Recommendations


Start node End node PPT Word IE FF Chrome TextEdit Recommendation
Text Text delete(rangeContents) delete(rangeContents) delete(rangeContents) delete(rangeContents) delete(rangeContents) delete(rangeContents) delete(rangeContents)
inline inline delete(rangeContents)cursor: end of start delete(rangeContents)cursor: end of start delete(rangeContents)cursor: end of start delete(rangeContents)cursor: end of start delete(rangeContents)cursor: end of start delete(rangeContents)cursor: end of start delete(rangeContents)cursor: end of start
DEFAULT BACKSPACEinline/block/header/li(any combo that isn't inline to inline) DEFAULT BACKSPACEinline/block/header/li(any combo that isn't inline to inline) delete(rangeContents)merge + preserve stylingcursor: end of start delete(rangeContents)no mergecursor: end of startNote: Word 2003delete(rangeContents) + backwards merge delete(rangeContents)merge + preserve stylingcursor: end of start delete(rangeContents)no mergecursor: end of start delete(rangeContents)merge + preserve stylingcursor: end of start delete(rangeContents)merge + preserve stylingcursor: end of start delete(rangeContents)merge + preserve stylecursor: end of start
inline/block table cell N/A delete(rangeContent)backwards mergecursor: end of start no content changecursor: collapse(true) DEFAULT BACKSPACE delete(rangeContents)no mergecursor: end of start delete(rangeContents)no mergecursor: start of end delete(rangeContents)no mergecursor: end of start
table cell inline/block N/A delete(rangeContents)no mergecursor: start of end no content changecursor: collapse(true) DEFAULT BACKSPACE DEFAULT BACKSPACE delete(rangeContents)no mergecursor: start of end, but once you type, contents jump back up into table cell. delete(rangeContents)no mergecursor: end of start
table cell table cell delete cell contentscursor: collapse(true) UI prompt - delete entire row|column, shift cells up|left(unless table cells are in different tables, then does delete(rangeContents) + merge) no content changecursor: collapse(true) DEFAULT BACKSPACE delete(rangeContents)no mergecursor: end of start delete(rangeContents)no mergecursor: start of end,but if you type, cells merge delete(rangeContents)no mergecursor: end of start
li li DEFAULT BACKSPACE DEFAULT BACKSPACE[#FOOTNOTE-8 8] DEFAULT BACKSPACE delete(rangeContents)merge DEFAULT BACKSPACE DEFAULT BACKSPACE DEFAULT BACKSPACE

Delete

Recommendation for HTML 5:

Like in the collapsed cursor case, delete and backspace should be the same.

Current exceptions:

  • Firefox, only change is in cursor placement. With backspace, cursor goes at the end of the start node. With delete, it goes at the start of the end node. This subtle change is noticeable when you type after performing a backspace/delete - does it take the style of the first or second original node?
  • IE, only change is with table cells. With backspace, contents are not changed, but the cursor is collapsed. With delete, it is a complete nop.
  • Word, only change is with table cell to table cell selection. With backspace, a prompt is raised with several options on what to do (delete entire row, delete entire column, shift cells up, etc). With delete, the cell contents are just deleted and the cursor is collapsed to start.
  • IE 8 is different from IE 7 for both backspace and delete cases. The tables above show IE 7 behavior. Biggest difference is that IE 8 uses directionality information and does different cursor placement for backspace/delete. Behavior seems to be: forward selection + backspace: end of start, forward selection + delete, backward selection + backspace, backwards selection + delete: start of end

execCommand('ForwardDelete') with a range selected should be equivalent to delete with the same range selected.


Tab

[#FOOTNOTE-9 9]

Notes:

  • FF 3, IE 7, Chrome 1 always perform focus next for tab, no matter what context. They have been eliminated from the tables below to avoid clutter.
  • I've combined Word in Doc mode and HTML mode into one column. Only differences are with tab vs spaces.

Recommendation for HTML 5:

This is largely context-specific, so recomendations are provided in the far right column of the table below.


execCommand('insertText', false, ' ' | '\t') should be equivalent to "replace selected(tab|spaces)" with the same range selected, which should be equivalent to calling execCommand('Delete') and then execCommand('insertText').

The assorted indent behaviors should be equivalent to execCommand('indent') when given the same selection, likewise, for the outdent behaviors and execCommand('outdent').


Current Default Behaviors and Recommendations:


Context PPT 2003WinPPT2007Win Word 2003 Win Doc/HTML modeWord 2007 Doc/HTML Mode FF2 (linux) Wordpad TextEdit Mac Recommended default behavior
Text content selected, inside a single paragraph replace selected(tab) replace selected(tab |10 spaces) replace selected(4 spaces) replace selected(tab) replace selected(tab) replace selected(tab|spaces)
Text content selected, inside a single paragraph, SHIFT+TAB replace selected(tab) replace selected(tab | 10 spaces) focus prev replace selected(tab) nop replace selected(tab|spaces)
Text content selected, span 2 paragraphs partially indent paragraphs Delete selectedindent paragraphcursor collapsed to start of paragraphundo: add entry(delete)add entry(indent) replace selected(4 spaces) replace selected(tab) replace selected(space) indent paragraphs
Text content selected, span 2 paragraphs partially, SHIFT + Tab outdent paragraph delete selectedoutdent paragraphcollapse cursor to start of paragraphundo: add entry(delete)add entry(outdent) focus prev replace selected(tab) nop outdent paragraphs
Text content, full paragraph(s)selected indent paragraph(s) indent paragraph(s) replace selected(4 spaces) replace selected(tab) replace selected(tab) indent paragraph(s)
text content, full paragraph(s) selected, SHIFT + TAB outdent paragraph(s) outdent paragraph(s) focus prev replace selected(tab) nop outdent paragraph(s)
text content, select 2 paragraphs, first paragraph fully selected, second partial indent paragraphs indent paragraphs replace selected(4 spaces) replace selected(tab) replace selected(tab) indent paragraphs
text content, select 2 paragraphs, first paragraph fully selected, second partial, SHIFT + TAB outdent paragraphs outdent paragraphs focus prev replace selected(tab) nop outdent paragraphs
text content, select 2 paragraphs, first paragraph partially selected, second fully indent paragraphs replace selected(tab | 10 spaces) replace selected(4 spaces) replace selected(tab) replace selected(space) indent paragraphs
text content, select 2 paragraphs, first paragraph partially selected, second fully, SHIFT + TAB outdent paragraphs replace selected(tab | 10 spaces) focus prev replace selected(tab) nop outdent paragraphs
contained inside single li replace selected(tab) replace selected(tab | 7 spaces) indent liORindent just the text node containing the selection. Seems to depend on underlying html. replace selected(tab) replace selected(tab) replace selected(tab | spaces)
contained inside single li, SHIFT + TAB replace selected(tab) replace selected(tab | 7 spaces) outdent replace selected(tab) outdent replace selected(tab | spaces)
partially spanning multiple li indent lis delete selectedindent licursor at start of liundo: add entry(delete)add entry(indent li) indent lis replace selected(tab) replace selected(tab) indent lis
partially spanning multiple li, SHIFT + TAB outdent lis delete selectedoutdent licursor at start of liundo: add entry(delete)add entry (outdent li) or add entry (move cursor) outdent lis replace selected(tab) outdent first li (regardless of direction)cursor collapsed at start of second li, no matter ohw many selected outdent lis
li(s) fully selected indent li(s) indent li(s) ORif contains first li, indent list indent li(s) replace selected(tab) if one: indent li, collapse cursor to start of liif multiple: indent first li, collapse cursor to start of second li indent li(s)
li(s) fully selected, SHIFT + TAB outdent li(s) outdent li(s) OR if contsint the first li, outdent list outdent li(s) replace selected(tab) if one: outdent liif multiple: outdent first li, collapse cursor to start of second li outdent li(s)
spanning multiple li's, first li fully selected indent lis indent lis ORif contains first li, indent list indent lis replace selected(tab) indent first li indent lis
spanning multiple li's, first li fully selected,SHIFT + TAB outdent lis outdent li(s) outdent lis replace selected(tab) outdent first li outdent lis
spanning multiple li's, first li not fully selected indent lis delete selectedindent licursor at start of liundo: add entry(delete)add entry(indent) indent lis replace selected(tab) replace selected(tab) indent lis
spanning multiple li's, first li not fully selected, SHIFT + TAB outdent lis delete selectedoutdent licursor at start of liundo: add entry(delete)add entry(outdent) or addentry(move cursor) outdent lis replace selected(tab) outdent first li outdent lis
table cell - inside one cell focus next cell / insert table row (same as collapsed) focus next cell / insert table row (same as collapsed) focus next cell / insert table row focus next cell / insert table row focus next cell / move to next line focus next cell / insert table row
table cell - span 2 cells cannot select partial multiple cellsfocus moves to first cell in selection, and that cell is fully selected cannot select partial multiple cellsfocus moves to first cell in selection, and that cell is fully selected focus moves to second selected cell.cursor collapsed cannot select partial multiple cellsforward selection: focus is moved 2 cells past the last selected cell, cell is fully selectedbackwards selection: focus is moved 1 cel past the last selected cell (document order), cell is fully selectedinsert table row if focus would move to cell in a row that doesn't exist collapse cursor to start of second cell, no matter how many selected focus next cell
PRE N/A replace (3 spaces) replace (\t) N/A replace (\t) replace (tab | spaces)
CODE N/A replace (1 space) replace (4 spaces) N/A replace (\t)

Enter

[#FOOTNOTE-10 10]

Recomendation for HTML 5:

Perform a delete then an enter (as defined above in Part I). No exceptions. Undo: Behaves the same as regular text insertion. (shift|option) + enter: inserts a br.

Notes:

  • Word 2008 - Basically the same as a backspace (not a forward-delete) followed by an enter. It does something different if the text is selected via an undo command (it pretends the selection is collapsed at the end of the selection). It also does something different for tables. Iff the selection is partially in a table then the selection is just collapsed to the beginning. If the selection spans multiple TDs, then the selection is shrunk down to the first TD and then a regular enter is performed within that TD.
  • Behaviors are marked green in the table below if they match this recommendation even if their delete or enter implementations do not match those recommendations.

Current Default Behaviors:


Context Word 2008 Mac Doc and HTML mode IE 7 FF 3 - Win Chrome Wordpad textedit Mac RTF/HTML mode
Base behavior backspace (not forward-delete), then caret enter - maintain inline formatting Delete then caret enter - maintain inline formatting Delete then caret enter - inconsistent about maintaining inline formatting, but mostly does not keep any Delete then caret enter - maintains some inline formatting (e.g. sometimes loses background-color) Delete then caret enter except it loses inline formatting Delete/backspace then caret enter - maintain inline formatting
After Undo/Redo Collapse to end of the selection, then perform enter.
Selection starts before table ends in table Just collapse to beginning of selection. noop Delete all the text in the TDs, truncate selection to not include the table, do a regular delete+enter Delete all the text in the TDs, truncate selection to not include the table, do a regular delete+enter delete + enter Shrink selection to end just before the table and perform enter
Entire table row selected deletes the contents of the first selected cell, collapses cursor in that cell, then performs enter nop delete all text in the tds, collapse to start, regular delete + enter Any time a selection contains an entire table row, enter deletes that row delete row
Selection starts in a table and ends after it Deletes the selected rows, performs a backspace, then enter. noop Delete all the text in the TDs and the nodes selected after the table, then perform enter in the first TD Delete all the text in the TDs and the nodes selected after the table, then perform enter in the first TD delete + enter Shrink selection to end at the end of the first TD selected and perform enter.
Selection spans multiple TDs Collapse the selection to the first TD and then perform enter. noop Delete all the TD's contents and perform enter in the first TD Delete all the TD's contents and perform enter in the first TD move selection to first cell not in selection, perform enter Shrink selection to end at the end of the first TD selected and perform enter.
Entire table-contents selected Delete the table and then perform enter. noop Delete table contents Delete the table and perform enter. delete the table + enter Delete all the TDs in the table and leave just one TD, then perform enter.
Entire table selected Delete the table and then perform enter. Delete the table and perform enter. Delete table perform enter Delete the table and perform enter. delete + enter Delete the table and perform enter.
link Delete, then split the link (not delete + enter, since enter in a link in word navigates to the link) delete + enter delete + enter delete + enter delete + enter
li contents delete + enter delete + enter delete + enter Delete + enter if fully selected,delete li contents, add a new liif partial selection,delete + enter
node contents + line break after li delete then enter, but the delete does funky things


Appendix

Common Behavior Implementation Differences and Recomendations


Context PPT Word WordHTML mode FF2 (Linux) Wordpad TextEdit RTF mode TextEdit Mac mode Recommended default behavior
indent li if one type of list - follow typeif mixed types but indenting one type-follows type of li being indentedif mixed types, indenting mixed types-follows mixed types (not collapsed cursor) one type -follows typemixed types, single type selected - follows top typemixed types, multiple types selected - follows mixed types(not collapsed cursor) one type -follows typemixed types, single type selected - follows top typemixed types, multiple types selected - follows mixed types(not collapsed cursor) if one type - follow typemixed types, single type selected-follow typemixed types, mixed types selected -follows mixed types(not collapsed cursor) N/A always UL always UL if one type - follow typeif mixed type - follow selected type
insert spaces N/A N/A Inserts a span with spaces (variable # &nbsp):<span style='mso-tab-count:1'> </span> "    " or "   <br>" if end of block. N/A N/A <span> </span> UA specific
focus next cell moves focus to next table cell (next cell in row or first cell in next row)selects all text in that cell moves focus to next table cell (next cell in row or first cell in next row)selects all text in that cell moves focus to next table cell (next cell in row or first cell in next row)selects all text in that cell focus moves to next table cellcursor collapsed at start of that cell moves focus to next table cell (next cell in row or first cell in next row)selects all text in that cell moves focus to next table cellcursor collapsed at start of cell moves focus to next table cellcursor collapsed at start of cell moves focus to next table cellcursor selecting all text in cell


Notes

1) For more details, see Eric's original research.

2) For more details, see Eric's original research and Julie's original research. In this section, data is from Chrome 2.0.170.0.

3) Tested on IE 7, FF 3, Chrome 2, Word 2003, all on Windows.

4) For more details, see Julie's original research

5) For more details, see Ojan's original research

6) For more details, see Eric's original research and Julie's original research.

7) For more details, see Eric's original research and Julie's original research.

8) Does perform this UA's DEFAULT BACKSPACE behavior, but that doesn't match recommended DEFAULT BACKSPACE behavior.

9) For more details, see Julie's original research.

10) For more details, see Ojan's original research