2

I've got a simple input field as follows:

<input type="text" name="search_location" id="search_location" placeholder="Any Location" autocomplete="off">

Using jQuery, I'm trying to get all styles associated with it. The code for this looks like this (got it from this stackoverflow thread):

// Get all styles associated with element
var style = css(jQuery("#elementToGetAllCSS"));

function css(a) {
    var sheets = document.styleSheets, o = {};
    for (var i in sheets) {
        var rules = sheets[i].rules || sheets[i].cssRules;
        for (var r in rules) {
            if (a.is(rules[r].selectorText)) {
                o = $.extend(o, css2json(rules[r].style), css2json(a.attr('style')));
            }
        }
    }
    return o;
}

function css2json(css) {
    var s = {};
    if (!css) return s;
    if (css instanceof CSSStyleDeclaration) {
        for (var i in css) {
            if ((css[i]).toLowerCase) {
                s[(css[i]).toLowerCase()] = (css[css[i]]);
            }
        }
    } else if (typeof css == "string") {
        css = css.split("; ");
        for (var i in css) {
            var l = css[i].split(": ");
            s[l[0].toLowerCase()] = (l[1]);
        }
    }
    return s;
}

// Apply styles to target element
var style = css(jQuery("#elementToGetAllCSS"));
jQuery("#search_location").css(style);

Everything is peachy, except when it comes to CSS pseudo selectors like :before and :after at which point I get an "unrecognized expression" console error.

Any ideas?

EDIT - My Solution

The answer below will work, although I'm a bit concerned about how future proof the answer will be. With that in mind, there's an alternative that seems to be working fine:

$.fn.getStyleObject = function(){
        var dom = this.get(0);
        var style;
        var returns = {};
        if(window.getComputedStyle){
            var camelize = function(a,b){
                return b.toUpperCase();
            };
            style = window.getComputedStyle(dom, null);
            for(var i = 0, l = style.length; i < l; i++){
                var prop = style[i];
                var camel = prop.replace(/\-([a-z])/g, camelize);
                var val = style.getPropertyValue(prop);
                returns[camel] = val;
            };
            return returns;
        };
        if(style = dom.currentStyle){
            for(var prop in style){
                returns[prop] = style[prop];
            };
            return returns;
        };
        return this.css();
    }

var styles = $('elementToGetAllCSS').getStyleObject();
  this.css('search_location);
Community
  • 1
  • 1
Tiny Giant Studios
  • 1,125
  • 3
  • 12
  • 27
  • 9
    pseudo elements only exists in CSS, you can't access them with javascript directly. – adeneo Jan 08 '14 at 21:48
  • Not sure if this will help, but when I have a javascript selector issue I check my cheat sheet at this website. I realize this doesnt answer your question but might help. http://www.tutorialspoint.com/jquery/jquery-selectors.htm – Cam Jan 08 '14 at 21:50
  • @Adeneo Any idea on how to bypass the pseudo elements in the code above? – Tiny Giant Studios Jan 08 '14 at 21:51
  • @Cam - it's not so much a jQuery selector issue, as it is a CSS selector issue :) – Tiny Giant Studios Jan 08 '14 at 21:52
  • have you tried $(this).is(':after'); just shooting in the dark here, not really understanding the issue. I guess you are trying to get all the values for it. – Cam Jan 08 '14 at 21:54
  • @Cam - I'm trying to get all the CSS rules assigned to an element across all stylesheets. I would then like to apply ALL those rules to another element altogether... – Tiny Giant Studios Jan 08 '14 at 22:00
  • Wait.. Why are you complicating this then. You can easily just string your css code. #thiselement , #thatElement , .reallyanotherElement { Your CSS here } – Cam Jan 08 '14 at 22:04
  • @Cam - Because I have no way of determining what the styles will be. The styles will change as they're generated from a WordPress options framework :) – Tiny Giant Studios Jan 08 '14 at 22:06
  • Oh. You should be posting this in the Wordpress forum, they probably have a solution that is way more conducive to your situation. IMO. – Cam Jan 08 '14 at 22:09

1 Answers1

2

You can filter out the pseudo selectors in the loop by checking if the selector contains ::

for (var r in rules) {
    if (typeof rules[r] == 'object' && 
        'selectorText' in rules[r] && 
        rules[r].selectorText.indexOf('::') == -1 &&
        rules[r].selectorText.indexOf(':hover') == -1
    ) {
        if (a.is(rules[r].selectorText)) {
           o = $.extend(o, css2json(rules[r].style), css2json(a.attr('style')));
        }
    }
}

FIDDLE

adeneo
  • 312,895
  • 29
  • 395
  • 388
  • thanks for the code; Just tried it and it's now throwing errors on :hover instead of :after or :before – Tiny Giant Studios Jan 08 '14 at 22:24
  • `:hover`, `:active` etc doesn't get the double `::`, so we have to filter for that as well, but it's the same principle, updated the answer – adeneo Jan 08 '14 at 22:30
  • Yep, that'll do the trick. I'm a bit concerned about how future-proof it'll be as there's new pseudo selectors coming into play all the time. I've since managed to figure something out that I believe will be a tad more future proof and have updated the original question :) – Tiny Giant Studios Jan 08 '14 at 22:36