Search code examples
javascripthtmlgoogle-chromecontenteditable

Content Editable with inner divs behave poorly when moved in Chrome


I am currently working on a project where I have a content editable div with inner HTML elements, such as inner divs. In chrome, I am getting weird behavior in a certain case, which behaves well in Firefox. In this example, I have a content editable, with an inner nested div, which I colored red:

Content Editable Div before editing

Now, if I navigate to the end of the first line and press (forward) delete, the expected behavior would be that the entire second line will push to the end of the first line. In firefox, this is what happens. However, in chrome, this happens:

Content Editable Div after editing

I have provided my code below. In this example, pressing the semicolon will place the red div at the current window selection. If you place the red inner div and keep typing it will go away, so after placing the red div, click somewhere else in the content editable with your mouse.

<!DOCTYPE html>
<head>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <div id="container">
        <div id="editor" contenteditable="true" style="display:inline-block"><div><br></div></div>
    </div>
    <script>
        document.getElementById("editor").addEventListener("keydown", (e) => {
            if (e.key === ";") {
                e.preventDefault();
                let range = window.getSelection().getRangeAt(0);
                range.collapse(false);

                let div = document.createElement("span");
                div.contentEditable = false;
                div.style.height = "27px";
                div.style.width = "27px";
                div.style.backgroundColor = "red";
                div.style.display = "inline-block";


                range.insertNode(div);
            }
        })
    </script>
</body>

Here is style.css

body {
    margin:0px;
    padding:0px;
    overflow-y:hidden;
}

#container {
    width:fit-content;
    height:80vh;
    padding:0px;
    margin:auto;
    margin-top:10vh;
    background-color:rgb(190, 190, 190);
}

#editor {
    width:calc(80vw - 20px);
    height:100%;
    padding:0px;
    margin:0px;
    background-color:rgb(221, 221, 221);
    font-size:24px;
    word-wrap: break-word;
    overflow-y:hidden;
    display:inline-block;
    border:none;
    outline: 0px solid transparent;
}

Solution

    1. Each browser has it's own default style so you shouldn't rely on the default behavior.
    2. There is no need to set css styles via javascript. Create a class with all your css styles and add the class via js.
    3. If you defined a css style in your css file, don't duplicate that css style in your html.


    index.html

    <!DOCTYPE html>
    <head>
        <link rel="stylesheet" type="text/css" href="style.css" />
    </head>
    <body>
        <div id="container">
            <div id="editor" contenteditable="true">
                <div><br /></div>
            </div>
        </div>
        <script>
            document.getElementById("editor").addEventListener("keydown", (e) => {
                if (e.key === ";") {
                    e.preventDefault();
    
                    let range = window.getSelection().getRangeAt(0);
                    range.collapse(false);
    
                    let div = document.createElement("span");
                    div.classList.add("red-block-spacer");
    
                    range.insertNode(div);
                }
            });
        </script>
    </body>
    

    style.css

    body {
        margin:0px;
        padding:0px;
        overflow-y:hidden;
    }
    
    #container {
        width:fit-content;
        height:80vh;
        padding:0px;
        margin:auto;
        margin-top:10vh;
        background-color:rgb(190, 190, 190);
    }
    
    #editor {
        width:calc(80vw - 20px);
        height:100%;
        padding:0px;
        margin:0px;
        background-color:rgb(221, 221, 221);
        font-size:24px;
        word-wrap: break-word;
        overflow-y:hidden;
        display:inline-block;
        border:none;
        outline: 0px solid transparent;
    }
    
    #editor > div,
    #editor > span {
      display: inline-block;
    }
    
    .red-block-spacer {
      display: inline-block;
      height: 27px;
      width: 27px;
      background-color: red;
    }