Bringing it all together: Copy With Style
Tags: javascript, webdev
Published: 2022-01-06 02:51:20

And now the tour is done, all that is left is to present the Copy With Style interface and how it is used.

We expose a single class `Copy_With_Style` that can be instantiated as follows in client side Javascript. For example:

```javascript
const clipboard = new Copy_With_Style({ button: document.getElementById("button_to_copy"),
									    element: document.getElementById("element_to_copy"),
 									    stylesheets: ["default.css"],
									  }); 
```

To clarify, this is yet more delectably clear and intuitive JavaScript notation (not)! It is the de facto standard all the same, for passing a list of optional, named arguments to a function (in this case, a [class](https://www.w3schools.com/Js/js_classes.asp) constructor).

It works using a JavaScript object which is an arbitrary container for attributes and is [described well enough by others](https://afontcu.medium.com/cool-javascript-9-named-arguments-functions-that-get-and-return-objects-337b6f8cfa07).

Important to note is only that in reality there is only one argument, which is an [object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) and can be written like so in Javascript:

```javascript
const myobj = {}
```

Looks a little like a [Python dictionary](https://www.w3schools.com/python/python_dictionaries.asp) to me but hey in JavaScript it's an object but looks and works the same way more or less as a dictionary. That is, it can contain properties and be [initialized](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer) as follows:

```javascript
const myobj = {prop1: val1, prop2: val2, prop3: val3}
```

JavaScript is a little more flexible here and you could write also:

```javascript
const myobj = new Object();
myobj.prop1 = val1;
myobj.prop2 = val2;
myobj.prop3 = val3;
```

What you need to know is that the single object argument that `Copy_With_Style` takes can have these attributes (default values are shown and if missing are mandatory arguments, or attributes or properties or whatever you like to call them):

| Argument                     | Default value          | Description                                                  |
| ---------------------------- | ---------------------- | ------------------------------------------------------------ |
| **button**                   | None                   | an [HTML element](https://developer.mozilla.org/en-US/docs/Glossary/Element) that can be clicked. Ideally a [button](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button) element. If it has a progress element as a child or sibling this can be used for monitoring [progress](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress) on style in-lining. Unnecessary unless you are copying very large HTML elements with many 10s or 100s of thousands of children. |
| **element**                  | None                   | an [HTML element](https://developer.mozilla.org/en-US/docs/Glossary/Element) that will be copied ()with all its children) to the clipboard when **button** is clicked. |
| **stylesheets**              | `[]`                   | An [array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) of strings that represent CSS style sheets. For example `["default.css", "extras.css"]`. If this is a non empty array then only styles from these sheets will be included in the copy. if you know your element only draws styles from specific sheets, then specifying them can speed up the style inlining and/or shrink the size of the copy. Otherwise all the stylesheets the document includes will be used. |
| **mode**                     | `"attribute"`          | A string. Either "attribute" or "tag". <br />If "tag" then a `<style>` tag is added to the copy and the **element**'s style attributes are left untouched.  This  is fast and can conserve pseudo elements like `:hover`.  Most email clients can't cope with this, but it will produce a rich copy in HTML contexts that do.<br />If "attribute" then the style attributes of **element** and all its children will be updated with style information taken from the **stylesheets** and the browsers computed styles, to produce as true a copy as possible. This produces a larger copy generally than "tag" but is respected by most email clients today. It's also a lot slower to produce if your element is large enough canhave significant processing costs. |
| **defer**                    | `[50000,0]`            | Meaningful only if **mode** == "attribute" and determines if and how often the style in-liner will defer to to UI to keep the UI responsive. Possible values are:<br />`true`: defer to the UI after ever element is processed. Not recommended, slows down processing immensely.<br />`false`: never defer to the UI while in-lining. Will lock the UI until finished. No problem for small elements, can be bothersome for very large elements.<br />`[threshold, frequency]`: The UI is deferred to only if more than `threshold` elements are being copied, and only once ever `frequency` elements are processed. If `frequency` is `0` and a **progress** bar is specified, it is is optimised to be number of elements per progress bar pixel |
| **triggers**                 | `["button"]`           | Meaningful only if **mode** == "attribute" and determines how and when style in-lining is triggered. This is an array of triggers and can contain:<br />`"button"`: to request that style in-lining happen when the copy **button** is clicked.<br />`"schedule"`: to schedule a style in-lining once the DOM is fully rendered.<br />`"observe"`: to request that **element** be observed, and if it's seen to change, then a style in-lining will be triggered. This is useful if element is responsive to user interactions. If **defer** is set to maintain a responsive UI any change to **element** will trigger a request for any existing in-lining to bail and start one anew.<br />Sensible combinations are:<br />`["button"]` for small and moderate elements.<br />`["schedule", "observe"]`  for extremely large elements. |
| **progress**                 | `false`                | Meaningful only if **mode** == "attribute" and requests that a progress bar be displayed to convey the progress of style in-lining. Accepts the following values:<br />`false`: no progress bar is used.<br />`true`: a progress bar is used if an HTML progress element is found as a sibling or child of **element**.<br />an HTML progress element: specify an element if you prefer and it will be used. <br />If  a progress bar is being used then **defer** is also set, to `[0, 0]` if it is not set (`false`) or the threshold is set to 0 if it is an Array of 2 elements. This is necessary because without a deferral to UI the progress bar will not update (render). |
| **copy_wrapper**             | `true`                 | **element** is wrapped in a simple [`<div>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div) with id `copy_me_with_style` before styling (either by tag or attribute as specified by **mode**). If `true` the wrapper will be place on the clipboard, if `false` only its contents will be (i.e **element** and any style information added). |
| **class_button**             | `"copy_with_style"`    | The CSS class assigned to the provided button. This is the buttons rest state though it conserves this class across all states. When clicked in this state the button will trigger a copy preparation if necessary and a copy of prepared data to the clipboard. |
| **class_preparing**          | `"preparing_for_copy"` | The CSS class assigned to the provided button when copy preparation is in progress. This may be very very quick (near instantaneous) or take some while, depending on choice of **mode** and size of the **element**. Most things are very fast, but "attribute" mode with very large elements can be slow. When in this state the button will either be disabled or trigger a restart of the preparation depending on configuration. |
| **class_ready**              | `"ready_to_copy"`      | The CSS class assigned to the provided button when copy preparation is in complete and a copy is ready to place on the clipboard. When in this state the button will simply copy the prepared texts and HTML to the clipboard. |
| **deep_exclusions**          | `null`                 | Meaningful only if **mode** == "attribute" and provides a function to call, which accepts an HTML element as its only argument, and returns true if that element and all its children should be excluded from the copy.<br />The default implementation excludes all hidden (not visible) elements. <br />If provided, this function replaces the default implementation. |
| **shallow_exclusions**       | `null`                 | Meaningful only if **mode** == "attribute" and provides a function to call, which accepts an HTML element as its only argument, and returns true if that element and and only that element should be excluded from the copy (its children are grafted onto the parent).<br />The default implementation excludes all all A tags that link internal to the site (href begins with /) and DIV tags that have the class "tooltip".<br />If provided, this function replaces the default implementation. |
| **extra_deep_exclusions**    | `null`                 | Identical to **deep_exclusions**,  except that it augments rather than replaces the default implementation. |
| **extra_shallow_exclusions** | `null`                 | Identical to **shallow_exclusions**,  except that it augments rather than replaces the default implementation. |
| **debug**                    | `false`                | If `true`, debugging information will be written to the console. Useful for checking the scheduling and observation and copy event triggers and such. Was used in developing and tuning this little class and remains in place for future use. |
| **log_performance**          | `false`                | If `true` will log style in-lining performance to the console. This was used to arrive at the performance statistics discussed above. |
| **log_HTML_to_console**      | `false`                | If `true` will log the styled HTML to the console, where it can be inspected. Useful for debugging if pasting brings no joy. |
| **log_text_to_console**      | `false`                | If `true` will log the styled text to the console, where it can be inspected. Useful for debugging if pasting brings no joy. |
| **check_clone_integrity**    | `false`                | When adding styles **element** is cloned and it is this clone that is styled and added to the clipboard. `true` request that after cloning its integrity is checked. It's never failed, and there's no reason it should, and this is unlikely to be of any great use. |
| **classes_to_debug**         | `[]`                   | An array of CSS [class](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class) names. If specified will break in the browser debugger during style in-lining when an element with one of the named classes is being processed. A nice way to drill down to specific classes to inspect the JavaScript variables if for any reason in-lining is not producing joy for a given class. |
| **styles_to_debug**          | `[]`                   | An array of [style](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/style) names. If specified will break in the browser debugger during style in-lining when an element with one of the named styles being applied is being processed. A nice way to drill down to specific styles to inspect the JavaScript variables if for any reason in-lining is not producing joy for a given class. if **classes_to_debug** are defined will break if both a class and style match are found. This can of course easily be tuned in code as needed. |

# Conclusion

And that brings this 12 part series to a conclusion, having introduced a small JavaScript class/library, Copy With Style:

https://github.com/bernd-wechner/Copy-with-Style/blob/master/README.md

It is currently 857 lines, albeit unminimised runs at 35kB thanks to what is hopefully clean documented code. It does drop to 16kB when minimised and comments all stripped:

https://www.minifier.org/
https://html-cleaner.com/js/

though few minimisers I tried cope with the class definition (bizarrre).

I hope it finds some use. After the [survey of existing options](https://dev.to/thumbone/in-lining-styles-client-side-9oi) failed to provide a sensible one for a client side Copy button, I was stuck writing one and it was a journey.