3

In CSS, .someclass { text-transform: uppercase } translates all text with class someclass to uppercase, but when I select the text, copy, and paste into a text editor or e-mail, the letters retain their original case. But CSS doesn't support transformations other than letter case as defined in Unicode. Comments and answers to my previous question Display one letter as another in page while copying untransformed text recommended to suck it up and use JavaScript because it cannot be done with CSS alone. (This would require some conspicuous notification that scripts have not been applied, such as CSS that gets hidden when script runs.)

In JavaScript without Flash, how would I substitute characters in all text nodes, but have the clipboard and search not reflect the transformation? For example, I might want to change r to w, such that ruby wings is displayed as wuby wings, but selecting the text, copying it, and pasting it into Notepad produces ruby wings, and Ctrl+F ruby finds this.

I would store the original text somewhere, trap copy events, count characters in the selected text, and copy the same range from the original text. But that doesn't help with Ctrl+F. And answers to How to copy to the clipboard in JavaScript? recommend the use of Zero Clipboard, which relies on Adobe Flash Player, which is not installed on mobile or libre browsers. Besides, oncopy is nonstandard as of this writing. Or must I also suck it up and use Flash and then have copy and find misbehave without Flash?

Community
  • 1
  • 1
Damian Yerrick
  • 4,602
  • 2
  • 26
  • 64
  • 2
    one thing you must understand first, CSS is just a styling markup, it doesnt change the html, only alters how things **Look like**. – Banana Oct 23 '14 at 17:37
  • @Banana I want to make `r` in body text look like `w` but behave like `r`. – Damian Yerrick Oct 23 '14 at 17:58
  • exactly so when you make the text look uppercase with css, it only looks that way. so if you want the text to become permanently uppercase, it cannot be done with css alone. – Banana Oct 23 '14 at 18:06

2 Answers2

0

Perhaps not the solution you were looking for, but have you considered creating a custom font? A quick Google search and you'll come up with quite a few options to do so...

Sparhawk_
  • 162
  • 1
  • 1
  • 9
  • Considered, yes. How would one go about obtaining the rights to a font to modify it? Or hiring a typographer to create a new font from scratch? Even if I were to learn typography myself, I'd first have to ask on Ask Ubuntu how to work around FontForge's broken PPA. – Damian Yerrick Nov 07 '14 at 16:58
  • Correction: type designer. (A typographer is one who *uses* a typeface.) – Damian Yerrick Dec 23 '16 at 03:52
0

The script needs to search text nodes for the letters in question, but skip text nodes in elements that aren't rendered as text, such as <style> and <script>. Then make the original letter invisible but copyable, and generate the replacement letter with CSS generated content.

To keep an invisible element copyable in Firefox, you can't use visibility: hidden or display: none. Instead use opacity: 0. Then use pointer-events: none to make it unclickable and position: absolute to suppress the space it took.

It actually takes three span elements to produce all the behaviors:

  • fuddinner hides the original letter
  • fudduc/fuddlc generates the new letter
  • fuddouter suppresses a word break before the new letter

Works consulted:

function classcontains(hayel, needle) {
    "use strict";
    needle = " " + needle + " ";
    var hayclass = " " + hayel.className + " ";
    return hayclass.replace(/[\n\t\r]/g, " ").indexOf(needle) > -1;
}

function fuddize(root) {
    "use strict";
    var textNodes = [];
    var contains = {}.hasOwnProperty;

    var blacklist = {
        br:1, hr:1,
        script:1, style:1, img:1, video:1, audio:1, canvas:1, svg:1, map:1, object:1,
        input:1, textarea:1, select:1, option:1, optgroup: 1, button:1
    };
    var replacements = [
        ["R","fudduc"], ["r","fuddlc"], ["L","fudduc"], ["l","fuddlc"]
    ];
    var outerclassname = "fuddouter";
    var innerclassname = "fuddinner";

    function addTextNodesInEl(el) {
        var tag = el.nodeName.toLowerCase();
        if (contains.call(blacklist, tag)
            || classcontains(el, innerclassname)) {
            console.log("Skipping "+tag);
            return;
        }
        for (var i = 0; i < el.childNodes.length; i++) {
            var n = el.childNodes[i];
            if (n.nodeType == Node.TEXT_NODE) {
                textNodes.push(n);
            }
        }
    }
    
    function fuddize_node(n) {
        var i = 0;
        while (i < replacements.length) {
            var needle = replacements[i][0];
            var txt = n.nodeValue;
            var idx = txt.indexOf(needle);
            if (idx < 0) {
                i = i + 1;
                continue;
            }
            var lpart = txt.substring(0, idx);
            var rpart = txt.substring(idx + needle.length);
            var papa = n.parentNode;
            if (lpart !== "") {
                var lnode = document.createTextNode(lpart);
                papa.insertBefore(lnode, n);
                fuddize_node(lnode);
            }
            var outerel = document.createElement("span");
            outerel.className = outerclassname;
            var midel = document.createElement("span");
            midel.className = replacements[i][1];
            var innerel = document.createElement("span");
            innerel.className = innerclassname;
            innerel.appendChild(document.createTextNode(needle));
            midel.appendChild(innerel);
            outerel.appendChild(midel);
            papa.insertBefore(outerel, n);
            n.nodeValue = rpart;
        }
    }

    if (!document.querySelectorAll) return false;
    var els = root.querySelectorAll('*');
    [].forEach.call(els, addTextNodesInEl);
    [].forEach.call(textNodes, fuddize_node);
}

function fuddize_body() {
    fuddize(document.body);
}

document.addEventListener('DOMContentLoaded', fuddize_body);
body { max-width: 32em; padding: 1em; line-height: 1.6; margin: 0 auto }
h1,h2,h3 { line-height: 1.2 }
h1 { margin-top: 0 }

.fuddinner { position: absolute; opacity: 0; pointer-events: none }
.fudduc:after { content: "W"; }
.fuddlc:after { content: "w"; }
.fuddouter { white-space: nowrap; }
<h1>Lorem Ipsum</h1>
<p>
Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure.
</p>
Community
  • 1
  • 1
Damian Yerrick
  • 4,602
  • 2
  • 26
  • 64