2

I am just about to start writing my own rich text editor but need to know if its possible to populate the text area and also how to save / use the data inside it.

I am currently using CKEditor, but its too clunky and large for what I want.
I'll be using this as a base: http://jsfiddle.net/Kxmaf/6/
I need to run certain checks on the data as well to check its length.

Code if needed:

// Stack Code Snippets does not work with Iframe. Here's the code anyways:

$(document).ready(function() {
  document.getElementById('textEditor').contentWindow.document.designMode = "on";
  document.getElementById('textEditor').contentWindow.document.close();
  
  $("#bold").click(function() {
    if ($(this).hasClass("selected")) {
      $(this).removeClass("selected");
    } else {
      $(this).addClass("selected");
    }
    boldIt();
  });
  $("#italic").click(function() {
    if ($(this).hasClass("selected")) {
      $(this).removeClass("selected");
    } else {
      $(this).addClass("selected");
    }
    ItalicIt();
  });
  $("#fonts").change(function() {
    changeFont($("#fonts").val());
  });
});

function boldIt() {
  var edit = document.getElementById("textEditor").contentWindow;
  edit.focus();
  edit.document.execCommand("bold", false, "");
  edit.focus();
}

function ItalicIt() {
  var edit = document.getElementById("textEditor").contentWindow;
  edit.focus();
  edit.document.execCommand("italic", false, "");
  edit.focus();
}

function changeFont(font) {
  var edit = document.getElementById("textEditor").contentWindow;
  edit.focus();
  edit.document.execCommand("FontName", false, font);
  edit.focus();
}
<a id="bold" class="font-bold">B</a>
<a id="italic" class="italic">I</a>
<select id="fonts">
  <option value="Arial">Arial</option>
  <option value="Comic Sans MS">Comic Sans MS</option>
  <option value="Courier New">Courier New</option>
  <option value="Monotype Corsiva">Monotype</option>
  <option value="Tahoma">Tahoma</option>
  <option value="Times">Times</option>
</select>
<br/>
<iframe id="textEditor" style="width:500px; height:170px;"></iframe>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Thanks.

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
Lovelock
  • 7,689
  • 19
  • 86
  • 186

1 Answers1

1

To create your Right Text Editor you could use the contenteditable attribute on a <div> element or via JS designMode. You don't need an <iframe>.

Note: while execCommand works on all major browser with its quirks and differences - it's now deprecated (but without any sane workaround but creating your own inline content editing software or by using Input-Events) so use with care.

To let you start from somewhere,

$(function() {

  var $editor = $('#textEditor');
  var $btn = $('span[data-cmd]');
  
  // EXECUTE COMMAND
  function execCmd(cmd, arg) {
    document.execCommand(cmd, false, arg);
  }

  $btn.mousedown(function(e) {
    e.preventDefault();
    $(this).toggleClass("selected");
    execCmd(this.dataset.cmd);
  });

  $("#fonts").change(function() {
    execCmd("FontName", this.value);
  });

});
* {margin: 0; padding: 0;}

#textEditorTab span {
  cursor: pointer;
  display: inline-block;
  padding: 0px 10px 0px 10px;
}

#textEditorTab span:hover {
  background: #eee;
}

#textEditorTab span.selected {
  background-color: orange;
}

#textEditor {
  width: 300px;
  height: 120px;
  border: 1px solid #ddd;
  padding: 5px;
}
<div id="textEditorTab">
  <span data-cmd="bold"><b>B</b></span>
  <span data-cmd="italic"><i>I</i></span>

  <select id="fonts">
    <option value="Arial">Arial</option>
    <option value="Comic Sans MS">Comic Sans MS</option>
    <option value="Courier New">Courier New</option>
    <option value="Monotype Corsiva">Monotype</option>
    <option value="Tahoma">Tahoma</option>
    <option value="Times">Times</option>
  </select>
</div>

<div id="textEditor" contenteditable>Lorem ipsum dolor sit amet</div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Now, I've seen that you want, on button click make it state ACTIVE, but if you think twice what you'll achieve is a mess for one reason:

User enters text -> selects a portion and clicks BOLD, now it jumps with the cursot to the beginning of line where there's no BOLD text... but wait, your button still has the active state...

to prevent such UX, you need to keep track of the child element the user has selected, and accordingly turn ON all the buttons from your options tab that match the selected tag criteria.

To not let you down - here's an example:

$(function() {

  var $editor = $('#textEditor').prop('contenteditable', true);
  var $btn = $('span[data-cmd]');
  var tag2cmd = {
    "B": 'bold',
    "I": 'italic'
  };

  // HIGHLIGHT BUTTONS on content selection
  function findParentNode(el) {

    $btn.removeClass('selected');

    var tagsArr = [];
    var testObj = el.parentNode || '';
    if (!testObj) {
      return;
    }

    var c = 1;
    tagsArr.push(el.nodeName);
    if (el.tagName != 'DIV') {
      while (testObj.tagName != 'DIV') {
        c++;
        tagsArr.push(testObj.nodeName);
        testObj = testObj.parentNode;
      }
      for (i = 0; i < c; i++) {
        $('[data-cmd="' + tag2cmd[tagsArr[i]] + '"]').addClass('selected');
      }
      console.log(tagsArr);
    }

  }
  // EXECUTE COMMAND
  function execCmd(cmd, arg) {
    document.execCommand(cmd, false, arg);
  }

  $btn.mousedown(function(e) {
    e.preventDefault();
    $(this).toggleClass("selected");
    execCmd(this.dataset.cmd);
  });

  $("#fonts").change(function() {
    execCmd("FontName", this.value);
  });


  // Having a button click toggleClass is not enough cause you'd also want 
  // to change button state when the user tlicks on different
  // elements tag inside the editor.

  var $lastSelected;
  $editor.on('mouseup', function(e) {
    e.stopPropagation();
    $lastSelected = e.target;
    findParentNode($lastSelected); // auto highlight select buttons
  });

});
* {margin: 0; padding: 0;}

#textEditorTab span {
  cursor: pointer;
  display: inline-block;
  padding: 0px 10px 0px 10px;
}

#textEditorTab span:hover {
  background: #eee;
}

#textEditorTab span.selected {
  background-color: orange;
}

#textEditor {
  width: 300px;
  height: 120px;
  border: 1px solid #ddd;
  padding: 5px;
}
<div id="textEditorTab">
  <span data-cmd="bold"><b>B</b></span>
  <span data-cmd="italic"><i>I</i></span>

  <select id="fonts">
    <option value="Arial">Arial</option>
    <option value="Comic Sans MS">Comic Sans MS</option>
    <option value="Courier New">Courier New</option>
    <option value="Monotype Corsiva">Monotype</option>
    <option value="Tahoma">Tahoma</option>
    <option value="Times">Times</option>
  </select>
</div>

<div id="textEditor">Lorem ipsum dolor sit amet</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

So you see, only one 'idea' and the code gets immediately huge, so if you really want to keep it simple, avoid such stuff and have fun!

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • Amazing answer! thankyou! And im making a basic blogging system so using a tex editor with basic functions for editing posts. Could just use ckEditor and the like, but i dont learn anything! – Lovelock Apr 17 '14 at 07:31
  • @user2921557 But really, try to keep it simple. People who work on the (3) most popular text-editors are actually in community, cause there's too much trickery involved in a proper way to make a `contenteditable` work crossbrowser due to all the differences between browsers and their understanding in editable elements, so they try constantly to keep up together with all that mess. So if you don't want to open that box (don't) really stick to colors, bold, italic, font size and that's it. For example just to make a proper way to handle `table` you'll need just extra 1000 lines :) – Roko C. Buljan Apr 17 '14 at 14:31
  • @user2921557 for a project of mine I'm building my own and before making some small steps i got twice to the *regret* point. Than I just decided to keep it simple and that was the best idea. – Roko C. Buljan Apr 17 '14 at 15:07