Clipboard API and HTML for email signature generator
Asked Answered
V

2

0

I'm building a simple email signature generator and I've run into the deprecated document.execCommand('copy') method.

I've got a div that holds the styled table for the footer, but when I run my try block and paste it into Outlook, the output is not what I expect.

const contentToCopy = document.getElementById(contentElId);
const range = new Range();
range.selectNode(contentToCopy);
window.getSelection().addRange(range);
// document.execCommand('copy');
await navigator.clipboard.writeText(contentToCopy);
window.getSelection().removeAllRanges();

I'm not sure what to pass into the writeText method.

  1. If I pass contentToCopy.innerText I don't get the anchor + image
  2. If I pass contentToCopy.innerHTML the HTML is escaped when I paste it into outlook.
  3. If I pass contentToCopy.textContent I get a bunch of weird spacing and no html (expected this)

When I trigger a copy myself (Cmd + C) and paste the result into Outlook, it works fine. How can I mirror that data with JS?

Additionally, I've seen this question and answer that says

"instruct the browser to pass the text in as text/html when the copy event fires."

However, I'm not sure how to accomplish this with the clipboard API.


If it helps, The contentToCopy in this case is a a div with a nested html table

<div id='contentToCopy'>
<table style="text-align: left; font-size: 14px; border:0; border-collapse: collapse; line-height: 25px;" cellpadding="0" cellspacing="0">
                <tbody style="border:0;">
                    <tr style="border:0;">
                        <td rowspan="6" style="border:0; padding-right:20px;">
                            <a href="https://www.choctawnation.com">
                                <img src="https://associates.choctawnation.com/wp-content/themes/bootscore-main/img/logo.png" width="170px" />
                            </a>
                        </td>
                        <td colspan="5" style="border:0;"><span style="border:0;" id="firstNameOutput">First Name</span> <span style="border:0;" id="lastNameOutput">Last
                                Name</span>
                        </td>
                    </tr>
                    <tr style="border:0;">
                        <td colspan="5" style="border:0;"><span style="border:0;" id="titleOutput">Job
                                Position</span> | <span id="departmentOutput" style="border:0;">Department</span>
                        </td>
                    </tr>
                    <tr style="border:0;">
                        <td colspan="5" style="border:0;">
                            <a href="tel:800-522-6170" id="mobileOutput">
                                <span style="border:0;">
                                    800-522-6170
                                </span>
                            </a>
                            <span id="mobileDivider" style="display: none;">|</span>
                            <a href="" id="desktopOutput">
                            </a>
                        </td>
                    </tr>
                    <tr>
                        <td colspan="5" style="border:0;">
                            <a id="emailOutput" href="mailto:[email protected]"><span style="border:0;">[email protected]</span>
                            </a>
                        </td>
                    </tr>
                </tbody>
            </table>
            <table style="text-align: left; font-size: 14px; border:0; border-collapse: collapse; line-height: 25px; padding-top:10px;" cellpadding="0" cellspacing="0">
                <tr>
                    <td rowspan="6" style="border:0;">
                        <a id="buttonOutput"><span style="border:0;"></span>
                        </a>
                    </td>
                </tr>
            </table>
            <table style="text-align: left; font-size: 14px; border:0; border-collapse: collapse; line-height: 25px; padding-top:10px;" cellpadding="0" cellspacing="0">
                <tbody id="disclaimerOutput" class="disclaimerText">
                </tbody>
            </table>
</div>
Vlad answered 5/12, 2023 at 21:37 Comment(0)
V
0

Answered on Reddit, and here's the solution I implemented.

/**
 * SHOULD NOT USE. This function technically works as of 12/20/23, and it relies on a deprecated `document.execCommand` method that is also implemented differently depending on the user's browser
 * @param contentToCopy The Container HTML element
 */
function deprecatedClipboardWriter(contentToCopy: HTMLElement) {
    const range = new Range();
    range.selectNode(contentToCopy);
    window.getSelection().addRange(range);
    document.execCommand('copy');
    window.getSelection().removeAllRanges();
}

/**
 * Creates a new Clipboard Item from the passed HTML Element and copies it to the user's clipboard.
 *
 * _**NOTE: Does not work in Firefox as of 12/20/23**_
 *
 * @param contentToCopy The container HTML Element
 */
async function modernClipboardWriter(contentToCopy: HTMLElement) {
    const data = [
        new ClipboardItem({
            ['text/html']: new Blob([contentToCopy.innerHTML], {
                type: 'text/html',
            }),
        }),
    ];
    await navigator.clipboard.write(data);
}
Vlad answered 20/12, 2023 at 18:39 Comment(0)
H
0

Instead of directly copying the HTML content, you can create a temporary textarea, set its value to the innerHTML of your content, and then copy the text from the textarea.

Here's an example:

const contentToCopy = document.getElementById(contentElId);
const textarea = document.createElement('textarea');
textarea.value = contentToCopy.innerHTML;
document.body.appendChild(textarea);
textarea.select();

try {
  await navigator.clipboard.writeText(textarea.value);
  console.log('Content copied to clipboard successfully');
} catch (err) {
  console.error('Unable to copy content to clipboard', err);
} finally {
  document.body.removeChild(textarea);
}
Homoeo answered 19/12, 2023 at 13:5 Comment(1)
This doesn't work, as it doesn't set the text type to 'text/html', thus when I paste it into Outlook, it pastes escaped html (which is not the behavior I'm looking for)Vlad
V
0

Answered on Reddit, and here's the solution I implemented.

/**
 * SHOULD NOT USE. This function technically works as of 12/20/23, and it relies on a deprecated `document.execCommand` method that is also implemented differently depending on the user's browser
 * @param contentToCopy The Container HTML element
 */
function deprecatedClipboardWriter(contentToCopy: HTMLElement) {
    const range = new Range();
    range.selectNode(contentToCopy);
    window.getSelection().addRange(range);
    document.execCommand('copy');
    window.getSelection().removeAllRanges();
}

/**
 * Creates a new Clipboard Item from the passed HTML Element and copies it to the user's clipboard.
 *
 * _**NOTE: Does not work in Firefox as of 12/20/23**_
 *
 * @param contentToCopy The container HTML Element
 */
async function modernClipboardWriter(contentToCopy: HTMLElement) {
    const data = [
        new ClipboardItem({
            ['text/html']: new Blob([contentToCopy.innerHTML], {
                type: 'text/html',
            }),
        }),
    ];
    await navigator.clipboard.write(data);
}
Vlad answered 20/12, 2023 at 18:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.