4

I'm trying to apply CSS to selected text. I tried the following and it doesn't work. I'm using Firefox.

$(document).keyup(function(){
savedRange = selection.getRangeAt(0);
$(savedRange).wrap('<span style="color:red"></span>');
});

I also tried

savedRange = selection.getRangeAt(0);
$(savedRange).css('color', 'red');

I can do this with contentEditable using execcommand, but execcommand applies html tags rather then inline styles. ex: <font/> instead of style="font..". I need to apply inline style and not deprecated html tags. I would like to use the jQuery css() property to apply styles.

Pinkie
  • 10,126
  • 22
  • 78
  • 124
  • The `.css('color', 'red');` should work correctly. Is there a better way to get the selected text? Also are you sure that the format is correct in `savedRange`? for example is it getting `font` or `#font` and so on. That would cause an issue – Soatl Apr 27 '11 at 19:21

3 Answers3

13

I'd recommend the CSS class applier module of my Rangy library for this. It works in all major browsers and for any selection. It will also toggle CSS classes on and off.

Here's an example from another question: How do I wrap a text selection from window.getSelection().getRangeAt(0) with an html tag?

Example:

<style type="text/css">
    span.red {
        color: red;
    }
</style>
<script type="text/javascript">
    var redApplier;

    window.onload = function() {
        rangy.init();
        redApplier = rangy.createCssClassApplier("red", true);
    };

    function makeSelectionRed() {
        redApplier.applyToSelection();
    }
</script>

UPDATE

If using classes isn't an option, you could still use a variation on this, although it's slightly roundabout: you could use Rangy to apply a class, and then use jQuery to find spans with this class and add your CSS to each. Here's an example:

function makeSelectionRed() {
    var randomCssClass = "rangyTemp_" + (+new Date());
    var classApplier = rangy.createCssClassApplier(randomCssClass, true);
    classApplier.applyToSelection();

    // Now use jQuery to add the CSS colour and remove the class
    $("." + randomCssClass).css({"color": "red"}).removeClass(randomCssClass);
}

jsFiddle: http://jsfiddle.net/z2mdw/2/

Community
  • 1
  • 1
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • 3
    @tim, i want to apply a css property in the form of inline style and not a css class. – Pinkie Apr 27 '11 at 22:33
  • Smart solution. Really like it. How do we toggle the CSS applied on/off as you have in your rangy examples. – Pinkie Apr 28 '11 at 00:07
  • I noticed, Although you are removing the class, we still see the empty class attribute in the span `Some `. After deleting the class, how do we detect if the class is empty and remove it. Otherwise this can create a mess of empty classes if not cleaned up afterwards. – Pinkie Apr 28 '11 at 01:36
  • @Pinkie: You could use `removeAttr("class")` instead of `removeClass(randomCssClass)`. However, without any classes in there, Rangy can't do the toggling stuff, I'm afraid. You could instead leave the class in and clean up before the page unloads (or whenever you want to clean up): http://jsfiddle.net/z2mdw/5/ . I'm working on a generic module for Rangy to do this kind of thing but it's a few weeks off. – Tim Down Apr 28 '11 at 08:36
  • @tim, Thanks. I worked around toggling using some conditionals. I guess i can check for empty classes last. The only thing i which was different is the 45k rangy filezize specially for what i'm doing. – Pinkie Apr 28 '11 at 18:51
  • Nice library, working well. The .toggleSelection() function is quite useful. For the record, .css({"color", "red"}) should be .css({"color": "red"}) – Andrew Sep 29 '14 at 14:25
  • @Andrew: Thanks. I've fixed the code in the answer. – Tim Down Sep 29 '14 at 15:59
6

This question thread about handling saved ranges may help. It doesn't specifically tell you how to add CSS, but it will help you wrap your range, and then you can probably chain a .css() function on top of that.

var range = window.getSelection().getRangeAt(0);
var newNode = document.createElement("span");
range.surroundContents(newNode);

Then you should be able to apply css to that span.

EDIT:

To apply CSS to the range selection, you can do the following. See my working example on jsfiddle.

You can set the CSS style on the span node directly with Javascript:

// Get the selection range
var range = window.getSelection().getRangeAt(0);

// create a new DOM node and set it's style property to red
var newNode = document.createElement('span');
newNode.style.color = "red";

// surround the selection with the new span tag
range.surroundContents(newNode); 

Or just surround the range with a span tag, and select that span tag with jQuery to use a nicer .css() syntax.

// get the selection
var range = window.getSelection().getRangeAt(0);

// create a new span node and give it an id 'testing'.
var newNode = document.createElement('span');
newNode.id = "testing";

// wrap the selection range with the <span id="testing"></span> node.
range.surroundContents(newNode);

// select that new node with jquery and use the jQuery .css() method to apply styles.
$("#testing").css("color", "green"); 

Obviously this javascript is not ideal for reuse as I hard coded an ID into the 2nd example, but hopefully you get the idea for use for your own needs.

Community
  • 1
  • 1
Dan Sorensen
  • 11,403
  • 19
  • 67
  • 100
  • I came close to the solution you provided, but I need to be able to apply css. – Pinkie Apr 27 '11 at 19:23
  • ok, I'll adjust the code above to show how to apply CSS to the selection. – Dan Sorensen Apr 27 '11 at 21:50
  • `surroundContents()` doesn't work in the general case. For example, if the start and end of the selected range are inside different elements, `surroundContents()` will throw an error. – Tim Down Apr 27 '11 at 22:07
  • @Tim - agreed, a library like your Rangy would be more bulletproof. But I figured this was good enough for a demo answer. ;-) – Dan Sorensen Apr 27 '11 at 22:09
  • @Dan: Oh yes, we did this the other day, didn't we? Sorry. – Tim Down Apr 27 '11 at 22:11
  • @Tim - although this solution will produce valid code, despite the errors since you won't have overlapping tags. ...so minor plus for coding, minus for user experience. ;-) – Dan Sorensen Apr 27 '11 at 22:12
  • @Tim - no worries. I'm just digging into your Rangy library now. :-) – Dan Sorensen Apr 27 '11 at 22:14
  • I've looked at rangy, it applies css class and not css property. I want to be able to apply css property inline using `css()`. – Pinkie Apr 27 '11 at 22:34
  • @dan hmm, i see allot of faults in this, althought it's a very close solution. Id's needs to be unique, applying css wihtout having an id would be a much better solution. Another thing is span keeps getting created if you tried to apply css multiple times to the same selected text. – Pinkie Apr 27 '11 at 22:45
  • @Pinkie - all true statements. This code isn't ideal for production, more of a proof of concept - it shows how to get from your starting point to inline CSS. I'll let you sort out the remaining details needed for your project. I really would use something like Rangy to do better DOM range selection. I prefer CSS classes myself, but I understand your project's requirements. – Dan Sorensen Apr 27 '11 at 23:08
  • @dan, i appreciate your input. You got my +1. Hopefully someone else can sort out these issues or come out with a better solution. I would've used rangy but it won't fit my needs. it only apply css classes not properties. Maybe @tim can sort this out. – Pinkie Apr 27 '11 at 23:14
1

Cant you do it with CSS alone?

http://css-tricks.com/overriding-the-default-text-selection-color-with-css/

janhartmann
  • 14,713
  • 15
  • 82
  • 138
  • 3
    This uses CSS3 and doesn't directly answer the questions. Also not cross browser compatible – Pinkie Apr 27 '11 at 19:24