The question is simple; using jQuery's css function, the computed style of a CSS attribute may be returned, but what if there are more than one style for that attribute being rendered? For example :

<div id="foo" style="text-decoration:underline;">Some underline text</div>

The instruction $('#foo').css('text-decoration'); will return underline. Now if I change it to

<div id="foo" style="text-decoration:underline;">Some underline <span id="bar" style="text-decoration:line-through;">text</span></div>

The instruction $('#bar').css('text-decoration'); will return line-through, alright.

But the actual text is also underline! How can I return both? Do I need to search all ancestors if I want to know if some text is both underline and line-through? Sounds a bit painful, no?

** Edit **

Another problem arises whith this HTML

<span style="text-decoration:underline;">some <span id="e1" style="font-weight:bold;">text</span></span>

where $('#e1').css('text-decoration'); returns none for some reason, while the text is clearly rendered with an underline.

** Disclaimer **

This question is not to debate how the UA renders an element, but if an element hierarchy applies a CSS or not. If one wants to understand text-decoration better, I suggest one would read about it. The question tries to focus on a more generalize matter. For example, it can also apply to this HTML

<div style="display:none;">Some <span id="keyword" style="text-decoration:underline;">hidden</span> text</div>

where one could want to know if the element keyword is visible or not. With the code below, this is simply done with

cssLookup($('#keyword'), 'display', 'none');   // -> true

** UPDATE **

After all the answers and comments, here is, based on Brock Adams solution :

/**
 * Lookup the given node and node's parents for the given style value. Returns boolean
 *
 * @param e     element (jQuery object)
 * @param style the style name
 * @param value the value to look for
 * @return boolean
 */  
function cssLookup(e, style, value) {
    var result = (e.css(style) == value);
    if (!result) {
        e.parents().each(function() {
            if ($(this).css(style) == value) {
                result = true;
                return false;
            }
        });
    }

    return result;
}

Thank you, everyone, for your inputs.

share|improve this question

I had some fun with this, but not confident that it meets your needs so maybe someone can improve upon it: jsfiddle.net/melee/fSESn/1 – Nic Jul 14 '11 at 4:34
1  
yes, the end result is achieved, but I'd like a more "generic solution", what if there are n elements between the two text-decoration now? – Yanick Rochon Jul 14 '11 at 4:36
feedback

4 Answers

up vote 2 down vote accepted

I don't think any browser, or the W3C, provides a good way to do this.

A complicating factor is knowing which styles cancel preceding styles (underline versus no-underline, for example).

So, we would need multiple look-up tables or human judgement to know which style actually applied.

Finally, all these methods (3 answers so far) cannot distinguish between a blank, or missing, style setting and an explicitly set none. Obviously the browser can render an explicitly set none differently than a blank or missing setting.

For human use, this code should do the trick:

function cssTree (jNode, styleName, bShowBlanks) {
    var styleArray  = [jNode.css (styleName)];

    jNode.parents ().map ( function () {
        var style   = $(this).css (styleName);

        if (bShowBlanks  ||  ! /^(none|\s*)$/i.test (style) )
            styleArray.push (style);
    } );
    return styleArray;
}

alert ( cssTree ( $("#bar"), 'text-decoration') );


See it in action at jsFiddle.

Results:

bar: line-through,underline
el: none,underline

//-- With bShowBlanks = true.
bar: line-through,underline,none,none
el: none,underline,none,none
share|improve this answer
I am choosing your answer as it is small and got me thinking about a solution (see my question edits) – Yanick Rochon Jul 14 '11 at 5:52
Thank you. Glad we were able to help. – Brock Adams Jul 14 '11 at 5:59
Still a problem here. The issue is that the spec says text-decoration doesn't inherit, but it's rendered as if it does. Try setting the inner span's text-decoration to none when the outer div is underline. The browser will still render the underline on the inner span. – Jason Striegel Jul 14 '11 at 6:18
@Jason, no, this is documented behavior : "The 'text-decoration' property on descendant elements cannot have any effect on the decoration of the ancestor." Usually, the UA will cut the element in two, splitting it to disable the text decoration. Have a child element trying to override the text-decoration is a violation and has no effect. – Yanick Rochon Jul 14 '11 at 6:29
@Yanick - Yes, that's correct. What I'm getting at is that if you want to know the way the text of an element is rendered, you need to ignore any ancestor "none"s and collect all of the text-decorations that have been applied up the tree. What browser are you using that disables text decoration if it's re-specified in a child? Firefox just sums them all up. – Jason Striegel Jul 14 '11 at 6:39
show 7 more comments
feedback

Here's a possible solution:

function getVisualCSS(tag, elem){
    var styles = [];
    var thisStyle = $(elem).css(tag);
    if(thisStyle != 'none') styles.push(thisStyle);
    while($(elem).parent()[0].tagName != 'BODY'){
        styles.push($(elem).parent().css(tag));
        elem = $(elem).parent();
    }
    styles.push($(elem).parent().css(tag));
    return $.unique($.grep(styles, function(n){
        return n != 'none';
    }));  
}

What it does is, that it checks all of an elements ancestors for a given CSS tag.

For the sake of an "cleaner" array, all none values are removed and only unique values are returned.

Here's a fiddle: http://jsfiddle.net/nslr/bXx46/2/

share|improve this answer
nice way to do this +1 :) – Vivek Jul 14 '11 at 5:23
1  
@nrabinowitz: Re: "Ugh - please ignore previous comment"... You can delete your previous comments. ;) – Brock Adams Jul 14 '11 at 5:44
@Brock - So true! Never had to before :). Thanks - – nrabinowitz Jul 14 '11 at 16:51
feedback

Ok, this is not the easy, elegant answer you're looking for, but I have a working option here: http://jsfiddle.net/nrabinowitz/Uu6p3/1/

It's similar to @Thor's in concept, but uses built-in jQuery functions to find all ancestors, map their text-decoration styles into an array, filter for unique values not equal to "none", and return the array of styles:

/**
 * Generic function to find all values for
 * CSS settings that allow multiple values.
 *
 * @param {String} selector    JQuery selector
 * @param {String} attr        CSS attribute to look for
 * @param {String[]} ignore    Values to ignore
 */
function findAllCssValues(selector, attr, ignore) {
    var temp = {};
    ignore = ignore || ['none'];
    return $.grep(
        // create the array of all values
        $(selector)
            // select element and all ancestors
            .parents().andSelf()
            // put all css attribute results into an array
            .map(function() {
                return $(this).css(attr)
            })
            // make it a regular Javascript array
            .toArray(),
        // now filter for unique values that aren't "none"
        function(val) {
            if (val in temp || $.inArray(val, ignore) >= 0) {
                return false;
            }
            temp[val] = true;
            return true;
        }
    );
}

findAllCssValues('#bar', 'text-decoration');

It works for all of your example snippets, as shown in the fiddle. I made it work for any CSS property, though I'm not sure this issue applies to anything but text-decoration.

share|improve this answer
feedback

If you try to set the inner span to text-decoration:none you'll notice the underline is still rendered from the parent div. The issue is that text-decoration doesn't inherit, but it's rendered as if it does.

So any text element will render the combination of all of its parents' text-decorations, even if it or any of its parents have text-decoration set to none.

If you want to know all of the text-decorations that have been applied to the text, you need to walk the chain right up to the top and record the combination of all decorations you encounter, excluding "none". Here's an example:

http://jsfiddle.net/fSESn/4/

function findDecoration( elem ) {
    var $elem = $(elem);
    var found = new Array();
    var dec = "";

    var current = $elem.css('text-decoration');

    if ( current != 'none' ) {
        found[ current ] = 1;
        dec = current;
    }

    var p = $elem.parent();
    while ( p != null && p.get(0).nodeName.toLowerCase() != 'body' ) {
        current = p.css('text-decoration');

        if ( current != 'none' && found[ current ] == null ) {
            found[ current ] = 1;
            dec = current + " " + dec;
        }
        p = p.parent();
    }

    return dec;
}
share|improve this answer
see my response to your last comment, under the accepted answer – Yanick Rochon Jul 14 '11 at 6:32
feedback

Your Answer

 
or
required, but never shown
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.