So, if we select and copy two paragraphs sitting next to each other, and then paste it in the text editor, there will be a blank line between them. If we do the same but with any other block elements, e.g. headers, there's no blank line. Why is it happening and how can I achieve this behaviour on other block elements?
Code (paragraphs):
<p>first paragraph</p>
<p>second paragraph</p>
Result (paragraphs):
> first paragraph
>
> second paragraph
Code (other elements):
<h3>first paragraph</h3>
<h3>second paragraph</h3>
Result (other elements):
> first paragraph
> second paragraph
Notice, there's no blank line between the lines in the second result.
textarea {
width: 30em;
height: 10em;
}
<h3>Select and copy these four lines of text</h3>
<div>Paste into the textarea below</div>
<p>There is a blank line after the P element</p>
<h4>What causes this?</h4>
<textarea></textarea>
Nothing in the Standard specifies how the selection in the rendered DOM should be converted to plain text, so this depends entirely on the application you copy from and/or the one you "paste" in.
There is 3.2.7 The innerText and outerText properties which says:
- If node is a
p
element, then append 2 (a required line break count) at the beginning and end of items.
But that is only relevant if you use .innerText
or .outerText
; however, this is not considered if the selection in the rendered DOM is converted to plain text because CSS there can affect the spacing or if there are new lines at all.
A clipboard can hold multiple representations of the data you copied.
On "paste" the application will check the clipboard for the best matching representation for the target (or on the desired behavior the user chooses for insert), and converts the data if necessary (with its own rules)
In JavaScript, you can overwrite what is stored in the clipboard upon copy:
function copyListener(event) {
event.clipboardData.setData("text/html", "here we have <strong>html</strong>");
event.clipboardData.setData("text/plain", 'this is just plain text');
event.preventDefault();
};
document.addEventListener("copy", copyListener, false);
.editor {
width: 30em;
height: 10em;
border: 1px solid rgb(200, 200, 200)
}
<p>copy me</p>
<p>
Paste as Plain:
</p>
<textarea class="editor"></textarea>
<p>
Paste as HTML:
</p>
<div contenteditable class="editor">
</div>
So as you can see here, something completely different to "copy me" is saved in the clipboard, and depending on whether you "paste" it to <textarea>
or contenteditable
you get the corresponding data.
This is also the case (for many applications and OSs) if you "paste" it into a different application. If you paste it "normally" it a RTE it would use the HTML version, or if you choose "Paste as plain text" or "Paste and Match style" which would use "text/plain". If no "text/plain" is present, it is up to the application that performs the paste to decide how to format it.
So if you want to have something consistent for the plain text you could use the html
as is and then just overwrite the text/plain
with your own converted text.
function copyListener(event) {
const range = window.getSelection().getRangeAt(0);
const rangeContents = range.cloneContents();
const wrapper = document.createElement('div')
wrapper.appendChild(rangeContents);
// store the actual HTML for rich text editors
event.clipboardData.setData("text/html", wrapper.innerHTML);
// store some custome plain text
event.clipboardData.setData("text/plain", 'my custom plain text');
event.preventDefault();
};
document.addEventListener("copy", copyListener, false);
.editor {
width: 30em;
height: 10em;
border: 1px solid rgb(200, 200, 200)
}
<h3>Select and copy these four lines of text</h3>
<div>Paste into the textarea below</div>
<p>There is a blank line after the P element</p>
<h4>What causes this?</h4>
<p>
Paste as Plain:
</p>
<textarea class="editor"></textarea>
<p>
Paste as HTML:
</p>
<div contenteditable class="editor">
</div>