Copy rich text

Copy formatted contents from given HTML with JavaScript

by Loilo

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.28.0/codemirror.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.28.0/codemirror.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.28.0/mode/xml/xml.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.28.0/mode/css/css.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.28.0/mode/htmlmixed/htmlmixed.min.js"></script>
<textarea id="html"><!-- The HTML you want to copy -->

Hello <b>jsFiddle</b> user!
</textarea>

<button><svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg></button>

CSS

html, body {
  margin: 0;
  height: 100%;
}

.CodeMirror {
  height: 100%;
  box-sizing: border-box;
}

.CodeMirror pre {
  font-family: 'Roboto Mono', Monaco, Menlo, Consolas, monospace;
  font-weight: inherit;
  font-size: 15px;
  line-height: 1.6;
}

.CodeMirror-focused .CodeMirror-selected,
.CodeMirror-line::selection,
.CodeMirror-line>span::selection,
.CodeMirror-line>span>span::selection {
    background: #c7e6ff;
}

button {
  position: fixed;
  width: 3.6em;
  height: 3.6em;
  top: 2em;
  right: 2em;
  box-sizing: border-box;
  z-index: 99;
  text-align: center;
  line-height: 1;
  background-color: #1c90f3;
  border: 0.2em solid white;
  border-radius: 50%;
  cursor: pointer;
}

button:focus {
  outline: none;
  border-color: #89c9ff;
}

button svg {
  position: absolute;
  left: 50%;
  top: 50%;
  margin: -12px 0 0 -12px;
}

@keyframes fading {
  from {
    opacity: 1;
    transform: scale(1.1);
  }
  to {
    opacity: 0;
    transform: scale(1.5);
  }
}

button.fading {
  animation: fading 0.5s;
  pointer-events: none;
}

JavaScript

// This is the actual copy function.
// It expects an HTML string to copy as rich text.

function copyFormatted (html) {
  // Create an iframe (isolated container) for the HTML
  var container = document.createElement('div')
  container.innerHTML = html
  
  // Hide element
  container.style.position = 'fixed'
  container.style.pointerEvents = 'none'
  container.style.opacity = 0

  // Detect all style sheets of the page
  var activeSheets = Array.prototype.slice.call(document.styleSheets)
    .filter(function (sheet) {
      return !sheet.disabled
  })

  // Mount the iframe to the DOM to make `contentWindow` available
  document.body.appendChild(container)

  // Copy to clipboard
  window.getSelection().removeAllRanges()
  
  var range = document.createRange()
  range.selectNode(container)
  window.getSelection().addRange(range)

  document.execCommand('copy')
  for (var i = 0; i < activeSheets.length; i++) activeSheets[i].disabled = true
  document.execCommand('copy')
  for (var i = 0; i < activeSheets.length; i++) activeSheets[i].disabled = false
  
  // Remove the iframe
  document.body.removeChild(container)
}



// Below is some setup for this fiddle. It's not relevant for the actual <copy function.

// Set up CodeMirror editors
var htmlEditor = CodeMirror.fromTextArea(html, {
    mode: 'text/html'
})

// Create nice animation on copy button click
document.querySelector('button').onclick = function () {
    var animatedClone = this.cloneNode(true)
  animatedClone.classList.add('fading')
  animatedClone.addEventListener('animationend', function () {
      animatedClone.parentNode.removeChild(animatedClone)
  })
  this.parentNode.appendChild(animatedClone)
  
  // Do the copy!
  console.log(htmlEditor.getValue())
  copyFormatted(htmlEditor.getValue())
}