2

I'm all new to this, but after spending a week trying to find an answer, I thought I would try asking directly. I am building a text editor using javascript and jquery. I have a textarea (with contenteditable), a stylesheet and a js script. What I want is that for each letter pressed, the kerning will be random. I achieved that with a simple function, but I don't want ALL textarea text to have this kerning, only the last letter pressed and so on and so on, so this type of thing would be the result:

simulation

There is what I have so far in my js file:

$(document).ready(

function() {
$('#textarea').keypress(function(){     
var KerningRandom =  Math.floor((Math.random()*90)-20);
$(this).css('letter-spacing',KerningRandom);

});

Here is my jsfiddle that actually doesn't work in jsfiddle and I don't get why as it works fine in local...?

Thanks!

Lea MC
  • 21
  • 1

2 Answers2

0

You cannot address individual characters ( and so glyphs ) in CSS. Only ::first-letter.

Options you have:

  1. convert all characters to individual spans. That's too much I think.
  2. use <canvas> to render text and so to implement text flow layout from scratch.
c-smile
  • 26,734
  • 7
  • 59
  • 86
  • Yes i thought about including each input letter in a span but the implications are endless I think... I tried using kerning.js to affect each letter (assigning a specific kerning to 'a', 'b' and so on) and telling kerning.js to set these kerning from a KerningRandom function (I hope I'm being clear, I'm not sure haha) might that work? As kerning.js can set specific kerning to specific letters I think it might... But I'm not a pro. Thank you so much for your answer, I will look into making the textarea a canvas, and will come back if it doesn't work (sorry I'm new to this...) Thanks again! – Lea MC Apr 10 '17 at 17:01
0

You can find a working plunker of what you want to achieve there (I forked yours).

https://jsfiddle.net/1gesLgsa/2/

Full code :

    //Code from https://stackoverflow.com/questions/1125292/how-to-move-cursor-to-end-of-contenteditable-entity


    //Namespace management idea from http://enterprisejquery.com/2010/10/how-good-c-habits-can-encourage-bad-javascript-habits-part-1/
    (function( cursorManager ) {

    //From: http://www.w3.org/TR/html-markup/syntax.html#syntax-elements
    var voidNodeTags = ['AREA', 'BASE', 'BR', 'COL', 'EMBED', 'HR', 'IMG', 'INPUT', 'KEYGEN', 'LINK', 'MENUITEM', 'META', 'PARAM', 'SOURCE', 'TRACK', 'WBR', 'BASEFONT', 'BGSOUND', 'FRAME', 'ISINDEX'];

    //From: https://stackoverflow.com/questions/237104/array-containsobj-in-javascript
    Array.prototype.contains = function(obj) {
        var i = this.length;
        while (i--) {
            if (this[i] === obj) {
                return true;
            }
        }
        return false;
    }

    //Basic idea from: https://stackoverflow.com/questions/19790442/test-if-an-element-can-contain-text
    function canContainText(node) {
        if(node.nodeType == 1) { //is an element node
            return !voidNodeTags.contains(node.nodeName);
        } else { //is not an element node
            return false;
        }
    };

    function getLastChildElement(el){
        var lc = el.lastChild;
        while(lc && lc.nodeType != 1) {
            if(lc.previousSibling)
                lc = lc.previousSibling;
            else
                break;
        }
        return lc;
    }

    //Based on Nico Burns's answer
    cursorManager.setEndOfContenteditable = function(contentEditableElement)
    {

        while(getLastChildElement(contentEditableElement) &&
              canContainText(getLastChildElement(contentEditableElement))) {
            contentEditableElement = getLastChildElement(contentEditableElement);
        }

        var range,selection;
        if(document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+
        {    
            range = document.createRange();//Create a range (a range is a like the selection but invisible)
            range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
            range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
            selection = window.getSelection();//get the selection object (allows you to change selection)
            selection.removeAllRanges();//remove any selections already made
            selection.addRange(range);//make the range you have just created the visible selection
        }
        else if(document.selection)//IE 8 and lower
        { 
            range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible)
            range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range
            range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
            range.select();//Select the range (make it the visible selection
        }
    }

}( window.cursorManager = window.cursorManager || {}));    



// ACTUAL CODE MADE FOR THIS ANSWER

    $('#textarea').keypress(function(event) {
    event.preventDefault();
      var KerningRandom = Math.floor((Math.random() * 90));
      if ($("#last").length > 0)
      {
      var previousLast = $("#textarea #last").html();
      $("#textarea #last").remove();
      }
      else
      var previousLast = "";
      $("#textarea").html($("#textarea").html().slice() + previousLast + "<span id='last'>" + String.fromCharCode(event.which) + "</span>")
      $("#last").css('margin-left', KerningRandom + "px");

var editableDiv = document.getElementById("textarea");
cursorManager.setEndOfContenteditable(editableDiv)
    });

var editableDiv = document.getElementById("textarea");
cursorManager.setEndOfContenteditable(editableDiv)

Point by point explanation :

     $('#textarea').keypress(function(event) {
    event.preventDefault();
      var KerningRandom = Math.floor((Math.random() * 90));
      if ($("#last").length > 0)
      {
      var previousLast = $("#textarea #last").html();
      $("#textarea #last").remove();
      }
      else
      var previousLast = "";
      $("#textarea").html($("#textarea").html() + previousLast + "<span id='last'>" + String.fromCharCode(event.which) + "</span>")
      $("#last").css('margin-left', KerningRandom + "px");

      var editableDiv = document.getElementById("textarea");
      cursorManager.setEndOfContenteditable(editableDiv)
    });

The event.preventDefault() prevent the letter to be added when pressing a key. Then, we calculate our left margin value, save the previous last letter we had and remove the span that contains the last letter as it's not the last letter anymore. We append the previous last letter , and the span that has a random left margin (to simulate the kerning) and the value of the pressed key (thanks to How to find out what character key is pressed?) to the actual content.

After that, we needed to move the carret at the end of the textarea manually, because it would stay at the beginning otherwise.

For that, I used the code from How to move cursor to end of contenteditable entity so goes there for explanation.

Community
  • 1
  • 1
Sorikairo
  • 798
  • 4
  • 19
  • Hi! Thanks for all this it looks great! The only problem I see is that each time a new key is pressed, the last letter's margin disappear and returns to original kerning doesn't it? How could I "fix" the margins/kerning? Again, I'm new to this, I hope you understand what I am trying to say... But the code you provided is a major step! I didn't think of looking at the kerning as margins... Thank you! – Lea MC Apr 10 '17 at 17:07
  • @Lea Exactly I thought the kerning had to be reset for the "non last" letter. What is exactly that you want to achieve then ? It won't be long :) – Sorikairo Apr 10 '17 at 23:20
  • @Lea MC Ok Sorry I didn't see the example picture you provided, I'll provide you a fixed code in 1 hour so every letter have a different kerning – Sorikairo Apr 10 '17 at 23:23
  • @LeaMC After several try, I fail to make it works properly, sorry that I cannot help more – Sorikairo Apr 11 '17 at 00:11
  • No worries, thank you very much for trying! I will try myself with different approaches and see if I can figure it out! Thank you! – Lea MC Apr 11 '17 at 07:41