Cursor alignment with custom list node view


I’ve created a simple todo list components, based on default ul/li and it generally works great, but for some reason cursor movement with arrows is inconsistent with normal lists.

On normal lists, if I move cursor with arrows - cursor maintains the same offset on each item. If cursor is located at the beginning of an item and I move up - cursor will move to the beginning of the item above. Same thing happens if cursor is in the middle of the item - it will tend to stay in the middle of each item as cursor is moved up and down.


On my list, cursor is always shifted to the beginning of the above/below item. This happens both with NodeView and toDOM rendering.

I’ve read the documentation, but couldn’t find how to control/fix cursor movements in such cases.

Can you please tell me how to achieve the correct cursor movement?

Thank you

// NodeView
export class CheckListItemView {
    constructor(public node: Node, public view: EditorView, public getPos: any) {
        this.dom = crel("li", {class: `checklist-item`},
                crel("input", {class: "checklist-item-checkbox", type: "checkbox"})
            this.contentDOM = crel("div", {class: "checklist-item-content"})
// Node spec
    check_list_item: {
        attrs: {
            checked: {default: false},
        parseDOM: [{tag: "li", attrs: {class: "checklist-item"}}],
        toDOM: () => {
            return [
                'li', { 'class': 'checklist-item' },
                    ['input', { class: 'checklist-item-checkbox'}],
                    ['div', { class: 'checkist-item-content' }, 0 ],
        content: "paragraph+ block*",

When you create an editable DOM structure (including styling) just like the one your node view produces without ProseMirror, does the problem exist there too? I can’t see how you’re styling the checkboxes and inner divs. But in principle ProseMirror shouldn’t interfere at all with up/down arrow keys in a situation like this, letting the browser handle them.

Yes, turned out to be a styling issue. Adding a contenteditable="false" to the input solved it.