Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

contenteditable - Selecting 2 child paragraphs and writing text over (Firefox Issue)

I have been searching on Google for over a week now, I've been trying to implement different solutions, but with no success, and it's bugging the hell out of me.

So you have a contenteditable div with several paragraphs(or other child elements of the same kind). Obviously this is the kind of layout you wanna keep. If the user selects two or more paragraphs and types text over it, it removes the paragraphs and sets the caret focus inside the parent div:

body {
  font-family: georgia;
}
.editable {
  color: red;
}
.editable p {
  color: #333;
}
.editable span {
  color: limegreen !important;
}
<div class="editable" contenteditable><p>paragraph one</p><p>paragraph two</p></div>

<hr>
  
<p>How to reproduce the bug:</p>
<ul>
  <li>Focus the contenteditable above by placing the cursor somewhere in one of the two paragraphs.</li>
  <li>press ctrl-a (in windows or linux) or cmd-a (in osx) to select-all</li>
  <li>type some text</li>
  <li>red text means that the text went directly inside the contenteditable div, black text means it went inside a paragraph</li>
</ul>
  
<p>The correct behaviour should be that that select-all and delete (or "type-over") in a contenteditable with only block tags should leave the cursor inside the first block tag.</p>
  
<p>Webkit gets this right, Firefox gets it wrong.</p>

I did try something like this in Jquery:

$(document).on('blur keyup','div[contenteditable="true"]',function(event){
    var sel = window.getSelection();
    var activeElement = sel.anchorNode.parentNode;
    var tagName = activeElement.tagName.toLowerCase();
    if (!(tagName == "p" || tagName == "span")) {
        console.log('not on editable area');
        event.preventDefault();
        //remove window selection
        var doselect = window.getSelection();
        doselect.removeAllRanges();
    }
});

So after blur or keyup event on contenteditable, detect where the caret position and if it's outside accepted editable areas stop the event or something?

I've tried changing the selection range, and a bunch of other stuff but maybe I'm just not seeing it. I'm sure a lot of people have had the same problem, but the only answers I found on Google or here is "content editable sucks", "why don't you just use an open source editor" and that kind of stuff.

Firefox weird behaviour: Inserting BR tags on break line I have also tried to remove Firefox'es weird behaviour with a function to remove all the <BR> tags firefox automatically inserts. Like this:

function removeBr(txteditor) {
    var brs = txteditor.getElementsByTagName("br");
    for (var i = 0; i < brs.length; i++) { brs[i].parentNode.removeChild(brs[i]); }
}

So I attached this to a keydown event, and it does exactly what it's expected, however that causes more weird behaviour (like preventing you adding spaces on selected paragraph).

Please vote up the question so we can raise more awareness.

I'm sure a lot of other people have bumped into the same problem, I think it would be good to know if there's a workaround or any "right" way to do it. I think it should really be discussed...

like image 347
just some guy Avatar asked Oct 19 '22 23:10

just some guy


2 Answers

So Firefox injects this abomination - <br></br> - into the contenteditable div when removing the paragraphs.

With a little bit of jQuery we can remove it and replace it with paragraph tags.

Current Limitation - The break tag seems to be injected only when removed with delete or backspace, not when typed over... consider this a concept :-)

Open this example in Firefox to test:

$("button").click(function() {

 $("br:not(p br)").replaceWith('<p>Write Me!</p>'); // dont replace when inside p
});
body {
  font-family: georgia;
}
.editable {
  color: red;
}
.editable p {
  color: #333;
}
.editable span {
  color: limegreen !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="editable" contenteditable>
  <p>paragraph one</p>
  <p>paragraph two</p>
</div>

<hr>

<p>Remove all text to trigger bug in Firefox and hit "fix problem!"</p>

</ul>

<p>Webkit gets this right, Firefox gets it wrong.</p>

<button class="fix">Fix Problem</button>
like image 171
misterManSam Avatar answered Oct 22 '22 13:10

misterManSam


Whats happening is this, when you select both groups of text and delete them you are also deleting all tags within the editable element. So your actually deleting the <p> tags from the div and then the only thing to write to is the div itself.

Why do you need two seperate paragraph tags? It would be easier to have the div by itself... Rather than setting the div as editable, have you tried setting the <p> tags as <p contenteditable="true">

like image 39
Nathan Thomas Avatar answered Oct 22 '22 13:10

Nathan Thomas