9

I'm wrapping the selected text in span tags, when you click a button. If I then select a different piece of text and click the button, that text also gets wrapped in tags. However, when I select a piece of text that's already wrapped in span tags, I'd like to remove those tags to unembolden the text, instead of wrapping those tags in more tags.

HTML

<div contenteditable="true" class="textEditor">Some random text.</div>

<a href="#" class="embolden">Bold</a>

JS

$('.embolden').click(function(){
    var highlight = window.getSelection();  
    var span = '<span class="bold">' + highlight + '</span>';
    var text = $('.textEditor').html();
    $('.textEditor').html(text.replace(highlight, span));
});

JSFiddle Demo

I'm probably getting greedy with this request but I select just part of a piece of text that's already wrapped in span tags, but not all of it, I'd like to close the original tag at the start of the selection, open a new tag right after that, then close the new tag at the end of the selection and open a new tag after that.

colmtuite
  • 4,311
  • 11
  • 45
  • 67
  • Are you targeting a specific browser? When I check `window.getSelection()` in Chrome, I get an object. –  Jan 02 '14 at 10:00
  • Not intentionally targeting a specific browser. I'd like the solution to work in very modern browsers. No need to support any IE or Opera. – colmtuite Jan 02 '14 at 10:04
  • Give me a little bit and I'll write you up a full solution to your goal -- it's a bit more complicated b/c contenteditable sections can have other tags in there as well. Edit: Doing the full solution b/c I think this could be useful for me, too. :) –  Jan 02 '14 at 10:16
  • Cool, looking forward to it mate :) – colmtuite Jan 02 '14 at 10:18
  • why not simply `document.execCommand('bold', false, null);` instead of spans? – Roko C. Buljan Jan 02 '14 at 10:33
  • Just heard of it now. Is it cross browser compatible? Could you provide a brief tutorial for triggering it with a click event please? – colmtuite Jan 02 '14 at 10:38
  • I'm getting close to finishing it... Not every command is a basic bold/italicize, etc... sometimes you have stuff specific to a stylesheet... what I'm creating will allow you to do more than what the execCommand supports. –  Jan 02 '14 at 10:56

8 Answers8

53

Why you are trying to bold text doing it by hand when you can use built in feature. Modern browsers implements execCommand function that allows to bold, underline etc. on text. You can write just:

$('.embolden').click(function(){
    document.execCommand('bold');
});

and selected text will be made bold and if it's already bold, the text styling will be removed.

A list of commands and a little doc can be found here. (More about browser support here).

$(document).ready(function() {
  $('#jBold').click(function() {
    document.execCommand('bold');
  });
});
#fake_textarea {
  width: 100%;
  height: 200px;
  border: 1px solid red;
}

button {
  font-weigth: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="jBold"><b>B</b></button>
<div id='fake_textarea' contenteditable>
  Select some text and click the button to make it bold...
  <br>Or write your own text
</div>
Andrew.Wolphoe
  • 420
  • 5
  • 18
Łukasz Jagodziński
  • 3,009
  • 2
  • 25
  • 33
  • Didn't realise it was an option until now. Could you provide a brief tutorial showing how to implement a basic command on a div? – colmtuite Jan 02 '14 at 10:42
  • I answered the question myself. But if you edit your answer with the Fiddle code I'll give you the tick. Thanks mate, that's awesome. – colmtuite Jan 02 '14 at 10:53
  • 1
    This is pretty sweet indeed, but do keep in mind that some browsers act a little bit different when emboldening text. Need to test all browsers when using this one. Bit of explanation can be found here: http://www.quirksmode.org/dom/execCommand.html – Hless Jan 02 '14 at 11:03
  • 1
    @Jagi Is it posibble to update your code? I know your answer is more then 1 year old, but jsfiddle can't be found :( – Stefan Aug 24 '15 at 05:38
  • i was struggling and found from your links – Khurram Ali Feb 18 '16 at 21:14
  • I'll point out that this is a poor solution. There isn't consistency across browsers with how the commands function. If I bold my selection in chrome and it wraps the content in a span with a bold style, and then load it in firefox that wraps it in bold tags, how do I edit the content in Firefox? ^Arbitrary example, not necessarily the exact behaviour of the browsers but highlights the problem. – Daniel Lane Jul 26 '16 at 08:35
  • That's really awesome. That link resolve many problems in my project. Thanks a lot – Sumeet Vishwas Oct 23 '17 at 13:11
  • But clicking directly causes focus loss on selected text, have to hold shift (Firefox 64.0 ) in order to be able to see the effect. What's the solution for that ? – Feelsbadman Dec 29 '18 at 16:44
  • To cancel format, you can also use `document.execCommand('removeFormat')` – Илья Зеленько Oct 26 '19 at 17:58
  • ```document.execCommand``` https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand#:~:text=Deprecated%3A%20This,at%20any%20time. is deprecated now. Any alternative for the same task?????? – Saurabh Raj Jan 12 '23 at 16:21
3

Copied function from this answer: Get parent element of a selected text

Haven't really perfected this and I think this only works on exact selections but it gives you an idea of how to go around this. The click function checks if the parent element of the current selection has the class 'bold', if so it replaces that element with the original selection again.

http://jsfiddle.net/XCb95/4/

jQuery(function($) {
    $('.embolden').click(function(){
        var highlight = window.getSelection();  
        var span = '<span class="bold">' + highlight + '</span>';
        var text = $('.textEditor').html();

       var parent = getSelectionParentElement();
        if($(parent).hasClass('bold')) {
              $('.textEditor').html(text.replace(span, highlight));
        } else {
            $('.textEditor').html(text.replace(highlight, span));
        }

    });
});

function getSelectionParentElement() {
    var parentEl = null, sel;
    if (window.getSelection) {
        sel = window.getSelection();
        if (sel.rangeCount) {
            parentEl = sel.getRangeAt(0).commonAncestorContainer;
            if (parentEl.nodeType != 1) {
                parentEl = parentEl.parentNode;
            }
        }
    } else if ( (sel = document.selection) && sel.type != "Control") {
        parentEl = sel.createRange().parentElement();
    }
    return parentEl;
}
Community
  • 1
  • 1
Hless
  • 3,326
  • 20
  • 22
1

Something like this should do the trick:

var span = '';

jQuery(function($) {
    $('.embolden').click(function(){
        var highlight = window.getSelection();
        if(highlight != ""){
            span = '<span class="bold">' + highlight + '</span>';
        }else{
            highlight = span;
            span = $('span.bold').html();
        }
        var text = $('.textEditor').html();
        $('.textEditor').html(text.replace(highlight, span));
    });
});
burktelefon
  • 988
  • 2
  • 8
  • 27
1

Got it, finally:

<!DOCTYPE html>
<html>
<head>
<style type="text/css">
.emphasized {
  text-decoration: underline;
  font-weight: bold;
  font-style: italic;
}
</style>
</head>
<body>
  <button type="button" onclick="applyTagwClass(this);" data-tag="span" data-tagClass="emphasized">Bold</button>
  <div contenteditable="true" class="textEditor">
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. In malesuada quis lorem non consequat. Proin diam magna, molestie nec leo non, sodales eleifend nibh. Suspendisse a tellus facilisis, adipiscing dui vitae, rutrum mi. Curabitur aliquet
      lorem quis augue laoreet feugiat. Nulla at volutpat enim, et facilisis velit. Nulla feugiat quis augue nec sodales. Nulla nunc elit, viverra nec cursus non, gravida ac leo. Proin vehicula tincidunt euismod.</p>
    <p>Suspendisse non consectetur arcu, ut ultricies nulla. Sed vel sem quis lacus faucibus interdum in sed quam. Nulla ullamcorper bibendum ornare. Proin placerat volutpat dignissim. Ut sit amet tellus enim. Nulla ut convallis quam. Morbi et
      sollicitudin nibh. Maecenas justo lectus, porta non felis eu, condimentum dictum nisi. Nulla eu nisi neque. Phasellus id sem congue, consequat lorem nec, tincidunt libero.</p>
    <p>Integer eu elit eu massa placerat venenatis nec in elit. Ut ullamcorper nec mauris et volutpat. Phasellus ullamcorper tristique quam. In pellentesque nisl eget arcu fermentum ornare. Aenean nisl augue, mollis nec tristique a, dapibus quis urna.
      Vivamus volutpat ullamcorper lectus, et malesuada risus adipiscing nec. Ut nec ligula orci. Morbi sollicitudin nunc tempus, vestibulum arcu nec, feugiat velit. Aenean scelerisque, ligula sed molestie iaculis, massa risus ultrices nisl, et placerat
      augue libero vitae est. Pellentesque ornare adipiscing massa eleifend fermentum. In fringilla accumsan lectus sit amet aliquam.</p>
  </div>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
  <script>
      function applyTagwClass(self) {
        var selection = window.getSelection();
        if (selection.rangeCount) {
          var text = selection.toString();
          var range = selection.getRangeAt(0);
          var parent = $(range.startContainer.parentNode);
          if (range.startOffset > 0 && parent.hasClass(self.attributes['data-tagClass'].value)) {
            var prefix = '<' + self.attributes['data-tag'].value + ' class="' + self.attributes['data-tagClass'].value + '">' + parent.html().substr(0,selection.anchorOffset) + '</' + self.attributes['data-tag'].value + '>';
            var suffix = '<' + self.attributes['data-tag'].value + ' class="' + self.attributes['data-tagClass'].value + '">' + parent.html().substr(selection.focusOffset) + '</' + self.attributes['data-tag'].value + '>';
            parent.replaceWith(prefix + text + suffix);
          } else {
            range.deleteContents();
            range.insertNode($('<' + self.attributes['data-tag'].value + ' class="' + self.attributes['data-tagClass'].value + '">' + text + '</' + self.attributes['data-tag'].value + '>')[0]);
            //Remove all empty elements (deleteContents leaves the HTML in place)
            $(self.attributes['data-tag'].value + '.' + self.attributes['data-tagClass'].value + ':empty').remove();
          }
        }
      }
    </script>
</body>
</html>

You'll notice that I extended the button to have a couple data- attributes. They should be rather self-explanatory.

This will also de-apply to subsections of the selected text which are within the currently-targeted element (everything goes by class name).

As you can see, I'm using a class which is a combination of things so this gives you more versatility.

  • Thanks for the answer mate. I gave you an upvote. I decided to give the tick to Jagi because there are still some bugs in your solution. For example, when you have two separate words emboldened with another word in between, then select all 3 words to embolden them all, it gets messed up. – colmtuite Jan 02 '14 at 11:35
  • @Moppy Good catch! I've adjusted the code above to correct for that scenario. –  Jan 02 '14 at 12:10
  • how to make it work with textarea instead of contenteditable div?@Jeremy Miller – Mithun Debnath Nov 20 '15 at 06:24
  • same question as above @JeremyMiller – Shreyan Mehta Apr 28 '17 at 18:46
1

If you are looking to use the keyboard to bold characters, you can use the following (Mac):

$(window).keydown(function(e) {
  if (e.keyCode >= 65 && e.keyCode <= 90) {
    var char = (e.metaKey ? '⌘-' : '') + String.fromCharCode(e.keyCode)
    if(char =='⌘-B') {
    document.execCommand('bold')
    }
  }
}) 

Using keyboard to bold character:

enter image description here

Cybernetic
  • 12,628
  • 16
  • 93
  • 132
0

This code goes thru the content of the textEditor and removes all the span tags. It should do the trick.

jQuery(function($) {
    $('.embolden').click(function(){
        $('.textEditor span').contents().unwrap();
        var highlight = window.getSelection();  
        var span = '<span class="bold">' + highlight + '</span>';
        var text = $('.textEditor').html();
        $('.textEditor').html(text.replace(highlight, span));
    });
});
Marcos
  • 527
  • 1
  • 5
  • 12
  • I believe the other span tags need to be retained, just handling the single enclosing... like if you had 5 paragraphs in there and 3 words were emboldened. –  Jan 02 '14 at 10:05
  • Thanks mate but any span tags outside the current selection need to be kept intact, so that solution won't work. – colmtuite Jan 02 '14 at 10:06
  • Oh, I see. Let me see if I can figure it out. I'll edit my answer when I do. – Marcos Jan 02 '14 at 10:08
0

Modern browsers utilize the execCommand function that allows you to embolden text very easily. It also provides other styles like underline etc.

<a href="#" onclick="emboldenFont()">Bold</a>

function emboldenFont() {
    document.execCommand('bold', false, null);
}
colmtuite
  • 4,311
  • 11
  • 45
  • 67
-1

check this is it what u wanted ???

using

.toggleclass()

(to make all text in text editor class bold only)

Karan Thakkar
  • 1,492
  • 2
  • 17
  • 24