Adam Burley
Adam Burley

Reputation: 6079

How do I get a css property only from inline styles using jQuery?

I'm trying to calculate the width of an element which has a hidden parent. Because the element is not visible, it returns a zero width. So I wrote this little function:

/**
  * Compute the width of an element if it were to be visible.
  *
  * $element: The element you want to compute the width of
  * $hiddenParent: A parent (ancestor) of $element which is currently hidden
  *
  * Returns: the width of $element if it were to be visible.
  */
var getDisplayedWidth = function($element, $hiddenParent) {
    var oldDisplay = $hiddenParent.css("display");
    $hiddenParent.show();
    var displayedWidth = $element.width();
    $hiddenParent.css("display",oldDisplay);
    return displayedWidth;
};

It works and returns the displayed width. But the problem is, the value of "oldDisplay" is not the real old value of the inline display property. It's the calculated value of the display property. (this is not a bug - it's totally what I would expect based on jQuery's documentation.)

This slight difference means that the method breaks down in the following use case - we have an element that is initially display:none but that is controlled through a class rather than an inline style. So oldDisplay gets set to "none". Now, when the display gets put back, the display:none becomes an inline style. Later in the execution, when some other javascript adds a class to make it visible, the inline style takes precedence and the element does not appear.

What I really want to do in this case is extract only the inline style version of the "display" property. If the element has no inline "display" property (as in the above example) then I want oldDisplay to be set to an empty string.

Now I've explained the background, the question simply put is this - how do I get a CSS property only from inline styles?

(PS: I know that I could do this by manually parsing the "style" attribute. But I feel there must be a better way, especially using jQuery.)

Upvotes: 1

Views: 1882

Answers (3)

Gabriel Hansen
Gabriel Hansen

Reputation: 45

The problem with doing this is that if a parent element is set to display:none, then all children will also show display:none as their computed style. So setting display visible on the element in question may not work unless you know that all parents are visible.

Also, you don't want to affect your elements style, either by overwriting it's previous inline display mode, or by overriding it's stylesheet declaration.

I wrote this little gem to solve both problems:

jQuery.fn.locate = function() {
var o = this, a = [],
    n = o.filter(':hidden').parents(':hidden').addBack()
        .each(function(i){ a[i] = $(this).get(0).style.display || '' })
        .css('display','block'),
    xy = o.offset(), p = [ xy.left - parseInt(o.css('margin-left'),10), o.width(), xy.top - parseInt(o.css('margin-top'),10), o.height() ]
    n.each(function(i){ $(this).get(0).style.display = a[i] })
    return {'left':p[0], 'right':p[0] + p[1], 'width':p[1], 'top':p[2], 'bottom':p[2] + p[3], 'height':p[3]}
}

This will search for all parent elements including the original element, record their inline style properties, set them all to display:block, record your original elements absolute positioning information, restore the inline styles on all affected elements, and then return an object with the positioning data. You can modify it to change the data it returns.

var t = $(elem).locate()
alert(t.width)

Here's a more complex version that will allow you to store the positioning data for a jQuery object containing multiple elements. It stores the info to each element's .data() attribute, and returns the original object to maintain chaining.

jQuery.fn.markLocation = function() {
var o = this, a = [],
    n = o.filter(':hidden').parents(':hidden').addBack()
        .each(function(i){ a[i] = $(this).get(0).style.display || '' })
        .css('display','block')
    o.each(function(){
        var t = $(this), xy = t.offset(),
            p = [ xy.left - parseInt(t.css('margin-left'),10), t.width(), xy.top - parseInt(t.css('margin-top'),10), t.height() ]
        t.data({'left':p[0], 'right':p[0] + p[1], 'width':p[1], 'top':p[2], 'bottom':p[2] + p[3], 'height':p[3]})
    })
    n.each(function(i){ $(this).get(0).style.display = a[i] })
    return o
}

To use it:

var t = $('elems').markLocation()
var d = t.find(singleElement).data()
alert(d.width)

And yes, I intentionally code in a heretical confusing manner, and never plan on changing :)

Enjoy

Upvotes: 2

Amogh Talpallikar
Amogh Talpallikar

Reputation: 12184

Some code to get separate styles in a json per style attribute.

$(selector,this).each(function(index)
{
   var styletag=$(this).attr('style');
   var stylestemp=styletag.split(';');
   var styles={};
   var c='';
   for (var x in stylestemp) {
     c=stylestemp[x].split(':');
     styles[$.trim(c[0])]=$.trim(c[1]);
   }
   // do whatever with the styles json here.
});

OR Try this as well:

$(".someSelector[style*=inline]").attr("myAttribute"); // give any selector with condition in square brackets which will filter out ones with inline styles.

Upvotes: 0

doubleswirve
doubleswirve

Reputation: 1428

If the style is set to display: none in the stylesheet, couldn't you just obtain the width like this:

function getElWidth(el, elParent){
  elParent.show() // inline display: block
  var w = el.width();
  elParent.attr('style', '');
  return w;
}

I guess this uses the style attribute, but just an idea.

Upvotes: 1

Related Questions