3

I don't know how this kind of editing is called, but I've seen it somewhere before. Lets say you have an article with a heading, body and footer. In your own WYSIWYG editor, you have three buttons for each article section, or maybe a dropdown with those options.

When you paste the content of the article into the editor, you want to use the buttons one after the other to divide the content into the sections you want. First, you click on the line you want to define as the heading. You click on any part of the line, doesn't matter. Now you click the heading button, and it defines the line where you currently are, and anything below it, as the heading. Now you click on the line you want to define as the body, and click the body button. Again, everything on the line, and below it, is defined as body. You do the same for footer.

Is something like that possible with execCommand?

noClue
  • 958
  • 1
  • 13
  • 34
  • Are you using some editor already? Or does it have to be just textarea? Do you want to wrap current line with some special header tag in place (textarea?) or just copy it somewhere else, to some variable let's say? – entio Dec 01 '17 at 15:03
  • @entio I've been looking at many web-based WYSIWYG editors, like CKEditor, but they are all overly "bloated". I don't need much functionality in my editor, hence why I wanted to do something from scratch, hence why I'm using contenteditable on a regular div. I used this [guide](https://code.tutsplus.com/tutorials/create-a-wysiwyg-editor-with-the-contenteditable-attribute--cms-25657) specifically. – noClue Dec 01 '17 at 16:03
  • @entio As for what I want to do specifically, lets say when you paste the content into the editable area, I want the whole content to be put under a new div with the class "undefined". Then, when you click on a line and select one of the article classes, body for example, the line the cursor was on and everything afterwards should effectively be taken out of that undefined div and into a new div called "body". – noClue Dec 01 '17 at 16:07
  • i still don't get the particular way you want the 'text picker' to work. Maybe you can provide example? Else try to point me in a right direction, that's fiddle i made: https://jsfiddle.net/entio/ux0c92xL/9/ let's use it as a starting point. – entio Dec 01 '17 at 16:32
  • @entio Thanks for trying to help. I just wish I wasn't as swamped with work, otherwise I would've gotten back to you more quickly. – noClue Dec 03 '17 at 17:47

3 Answers3

1

The open-source library wysiwyg.js takes cares of the heavy work with contenteditable divs, etc and allows you to create a frontend that suits your needs. You only need to write the code for the functions you need and you can put your own CSS styling on top of it. I wrote some example code using the jQuery version of library below, but there's plenty of information and examples in the documentation linked above. They also do have a pure JavaScript variant if that's easier for you to work with.

<button id="header">Header</button>
<button id="footer">Footer</button>
<textarea id="editor">Lorem ipsum dolor sit amet</textarea>

<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="http://path/to/wysiwyg.min.js"></script>
<script src="http://path/to/wysiwyg-editor.min.js"></script>

<script>
$(document).ready(function () {
    var editor = $("#editor").wysiwyg();

    $("#header").click(function () {
        editor.wysiwyg("shell").bold().fontSize(30);
    });

    $("#footer").click(function () {
        editor.wysiwyg("shell").italic();
    });
});
</script>

If you want to keep it simpler than that, Quill is a good option. It's still not as "heavy" as CKEditor and has an elegant interface. It also has built-in functionality for styling presets, such as headers and footers. This functionality also doesn't require you to highlight the line(s) you want to format, unlike wysiwyg.js. Though, this could be added into a wysiwyg.js solution with a custom function similar to this.

vqdave
  • 2,361
  • 1
  • 18
  • 36
  • This seems to be what I'm looking for. It's not *exactly* how I need it, but it seems customizable enough where I can add that functionality myself. Thanks! Glad to see someone could claim the bounty just in time. – noClue Dec 03 '17 at 17:45
1

For your own sanity, it's probably best to go for one of the popular wysiwyg editor libraries, but if you insist on rolling your own, it can be done in JavaScript. Use getSelection and getRangeAt to determine the cursor position, then bubble up to its parent block. Wrap that parent in a new div node underneath the editor.

Instructions: click one of the paragraphs, then click one of the buttons. You will need the web browser's developer toolbar to notice the difference.

It's still far from perfect; it works best when you work from top to bottom. And it cannot handle complex nesting scenarios; if you select a paragraph nested halfway within another paragraph, it may not capture all the way to the end.

window.wrap = function(section) {
    var isBlock = function(node) {
        return node.nodeType == 1 &&
               window.getComputedStyle(node).display == 'block';
    };
    var editor = document.getElementById('editor');
    var sel = window.getSelection();
    var range = sel && sel.rangeCount > 0 && sel.getRangeAt(0);
    if (range) {
        for (var node = range.startContainer;
             node && node.parentNode && node.parentNode !== editor &&
             (!isBlock(node) || node === node.parentNode.firstChild);
             node = node.parentNode);
        if (node && node.parentNode && isBlock(node)) {
            var wrapper = document.createElement('div');
            wrapper.className = section;
            var parent = node.parentNode;
            while (parent !== editor && parent === parent.parentNode.lastChild) {
                parent = parent.parentNode;   // break out of earlier wrapper
            } 
            while (node) {
                var next = node.nextSibling;
                wrapper.appendChild(node);
                node = next;
            }
            parent.appendChild(wrapper);
        }   
    }
}
<div>        
<button type="button" onclick="wrap('heading');">heading</button>
<button type="button" onclick="wrap('body');">body</button>
<button type="button" onclick="wrap('footer');">footer</button>
</div>      
<div id="editor" contenteditable="true" style="border:thin black solid; padding:4px">
<p>This is a sample article.</p>
<p>Of course, you can copy/paste your own content here.</p>
<p>But for your convenience, this content will work out of the box.</p>
<p>Let this be our footer.</p>
</div>  
Ruud Helderman
  • 10,563
  • 1
  • 26
  • 45
0

var editor = document.getElementById('editor');
var output = document.getElementById('output');
var format = document.getElementById('format');

var handleCursorChange = function () {
    //track cursor movement
}

editor.onclick = handleCursorChange;
editor.onkeydown = handleCursorChange;
editor.onfocus = handleCursorChange;

format.addEventListener("click", function (e) {
    if(e.target.id === "heading"){
        
        //format based on your requirement
    }
    if(e.target.id === "body"){
        
        //format based on your requirement
    }
    if(e.target.id === "footer"){
        
        //format based on your requirement
    }
});
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <textarea name="editor" id="editor" cols="30" rows="10"></textarea>
  <div id="format">
    <button id="heading">heading</button>
    <button id="body">body</button>
    <button id="footer">footer</button>
  </div>
  <div id="output"></div>
</body>

</html>
Pranay Kumar
  • 2,139
  • 1
  • 15
  • 15