De nombreux outils / API permettent de sélectionner des éléments de classes ou d'identifiants spécifiques. Il est également possible d'inspecter les feuilles de style brutes chargées par le navigateur.
Cependant, pour que les navigateurs rendent un élément, ils compileront toutes les règles CSS (éventuellement à partir de différents fichiers de feuille de style) et les appliqueront à l'élément. C'est ce que vous voyez avec Firebug ou WebKit Inspector - l'arbre d'héritage CSS complet pour un élément.
Comment puis-je reproduire cette fonctionnalité en JavaScript pur sans avoir besoin de plugins de navigateur supplémentaires?
Un exemple peut peut-être clarifier ce que je recherche:
<style type="text/css">
p { color :red; }
#description { font-size: 20px; }
</style>
<p id="description">Lorem ipsum</p>
Ici, l'élément de description p # a deux règles CSS appliquées: une couleur rouge et une taille de police de 20 px.
Je voudrais trouver la source d'où proviennent ces règles CSS calculées (la couleur vient de la règle p et ainsi de suite).
la source
Réponses:
Étant donné que cette question n'a actuellement pas de réponse légère (non-bibliothèque) et compatible avec tous les navigateurs, je vais essayer d'en fournir une:
function css(el) { var sheets = document.styleSheets, ret = []; el.matches = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector || el.oMatchesSelector; for (var i in sheets) { var rules = sheets[i].rules || sheets[i].cssRules; for (var r in rules) { if (el.matches(rules[r].selectorText)) { ret.push(rules[r].cssText); } } } return ret; }
JSFiddle: http://jsfiddle.net/HP326/6/
L'appel
css(document.getElementById('elementId'))
renverra un tableau avec un élément pour chaque règle CSS qui correspond à l'élément passé. Si vous souhaitez obtenir des informations plus spécifiques sur chaque règle, consultez la documentation de l' objet CSSRule .la source
a.matches
est défini dans cette ligne:a.matches = a.matches || a.webkitMatchesSelector || a.mozMatchesSelector || a.msMatchesSelector || a.oMatchesSelector
. Cela signifie que s'il existe déjà une méthode (standard) "matches" pour les nœuds DOM, il l'utilisera, sinon il essaiera d'utiliser la méthode spécifique à Webkit (webkitMatchesSelector), puis celles de Mozilla, Microsoft et Opera. Vous pouvez en savoir plus ici: developer.mozilla.org/en/docs/Web/API/Element/matchesif (a.matches(rules[r].selectorText))
condition de protection.css()
chacun des éléments parents.MODIFIER: Cette réponse est désormais obsolète et ne fonctionne plus dans Chrome 64+ . Partir pour le contexte historique. En fait, ce rapport de bogue renvoie à cette question pour des solutions alternatives à son utilisation.
Il semble que j'ai réussi à répondre à ma propre question après une autre heure de recherche.
C'est aussi simple que ça:
window.getMatchedCSSRules(document.getElementById("description"))
(Fonctionne dans WebKit / Chrome, peut-être d'autres aussi)
la source
Jetez un œil à cette bibliothèque, qui fait ce qui a été demandé: http://www.brothercake.com/site/resources/scripts/cssutilities/
Il fonctionne dans tous les navigateurs modernes jusqu'à IE6, peut vous donner des collections de règles et de propriétés comme Firebug (en fait, il est plus précis que Firebug), et peut également calculer la spécificité relative ou absolue de n'importe quelle règle. La seule mise en garde est que, bien qu'il comprenne les types de médias statiques, il ne comprend pas les requêtes de média.
la source
Version courte 12 avril 2017
Challenger apparaît.
var getMatchedCSSRules = (el, css = el.ownerDocument.styleSheets) => [].concat(...[...css].map(s => [...s.cssRules||[]])) /* 1 */ .filter(r => el.matches(r.selectorText)); /* 2 */
Line
/* 1 */
crée un tableau plat de toutes les règles.La ligne
/* 2 */
ignore les règles qui ne correspondent pas.Basé sur la fonction
css(el)
de @SB sur la même page.Exemple 1
var div = iframedoc.querySelector("#myelement"); var rules = getMatchedCSSRules(div, iframedoc.styleSheets); console.log(rules[0].parentStyleSheet.ownerNode, rules[0].cssText);
Exemple 2
var getMatchedCSSRules = (el, css = el.ownerDocument.styleSheets) => [].concat(...[...css].map(s => [...s.cssRules||[]])) .filter(r => el.matches(r.selectorText)); function Go(big,show) { var r = getMatchedCSSRules(big); PrintInfo: var f = (dd,rr,ee="\n") => dd + rr.cssText.slice(0,50) + ee; show.value += "--------------- Rules: ----------------\n"; show.value += f("Rule 1: ", r[0]); show.value += f("Rule 2: ", r[1]); show.value += f("Inline: ", big.style); show.value += f("Computed: ", getComputedStyle(big), "(…)\n"); show.value += "-------- Style element (HTML): --------\n"; show.value += r[0].parentStyleSheet.ownerNode.outerHTML; } Go(...document.querySelectorAll("#big,#show"));
.red {color: red;} #big {font-size: 20px;}
<h3 id="big" class="red" style="margin: 0">Lorem ipsum</h3> <textarea id="show" cols="70" rows="10"></textarea>
Les lacunes
@import
,@media
.Je vais peut-être aborder ces lacunes un jour.
Version longue 12 août 2018
Voici une implémentation beaucoup plus complète tirée de la page GitHub de quelqu'un (dérivée de ce code original , via Bugzilla ). Écrit pour Gecko et IE, mais il aurait également fonctionné avec Blink.
4 mai 2017: Le calculateur de spécificité a eu des bogues critiques que j'ai maintenant corrigés. (Je ne peux pas avertir les auteurs car je n'ai pas de compte GitHub.)
12 août 2018: les mises à jour récentes de Chrome semblent avoir découplé la portée de l'objet (
this
) des méthodes attribuées à des variables indépendantes. Par conséquent, l'invocationmatcher(selector)
a cessé de fonctionner. Le remplacer par l'matcher.call(el, selector)
a résolu.// polyfill window.getMatchedCSSRules() in FireFox 6+ if (typeof window.getMatchedCSSRules !== 'function') { var ELEMENT_RE = /[\w-]+/g, ID_RE = /#[\w-]+/g, CLASS_RE = /\.[\w-]+/g, ATTR_RE = /\[[^\]]+\]/g, // :not() pseudo-class does not add to specificity, but its content does as if it was outside it PSEUDO_CLASSES_RE = /\:(?!not)[\w-]+(\(.*\))?/g, PSEUDO_ELEMENTS_RE = /\:\:?(after|before|first-letter|first-line|selection)/g; // convert an array-like object to array function toArray(list) { return [].slice.call(list); } // handles extraction of `cssRules` as an `Array` from a stylesheet or something that behaves the same function getSheetRules(stylesheet) { var sheet_media = stylesheet.media && stylesheet.media.mediaText; // if this sheet is disabled skip it if ( stylesheet.disabled ) return []; // if this sheet's media is specified and doesn't match the viewport then skip it if ( sheet_media && sheet_media.length && ! window.matchMedia(sheet_media).matches ) return []; // get the style rules of this sheet return toArray(stylesheet.cssRules); } function _find(string, re) { var matches = string.match(re); return matches ? matches.length : 0; } // calculates the specificity of a given `selector` function calculateScore(selector) { var score = [0,0,0], parts = selector.split(' '), part, match; //TODO: clean the ':not' part since the last ELEMENT_RE will pick it up while (part = parts.shift(), typeof part == 'string') { // find all pseudo-elements match = _find(part, PSEUDO_ELEMENTS_RE); score[2] += match; // and remove them match && (part = part.replace(PSEUDO_ELEMENTS_RE, '')); // find all pseudo-classes match = _find(part, PSEUDO_CLASSES_RE); score[1] += match; // and remove them match && (part = part.replace(PSEUDO_CLASSES_RE, '')); // find all attributes match = _find(part, ATTR_RE); score[1] += match; // and remove them match && (part = part.replace(ATTR_RE, '')); // find all IDs match = _find(part, ID_RE); score[0] += match; // and remove them match && (part = part.replace(ID_RE, '')); // find all classes match = _find(part, CLASS_RE); score[1] += match; // and remove them match && (part = part.replace(CLASS_RE, '')); // find all elements score[2] += _find(part, ELEMENT_RE); } return parseInt(score.join(''), 10); } // returns the heights possible specificity score an element can get from a give rule's selectorText function getSpecificityScore(element, selector_text) { var selectors = selector_text.split(','), selector, score, result = 0; while (selector = selectors.shift()) { if (matchesSelector(element, selector)) { score = calculateScore(selector); result = score > result ? score : result; } } return result; } function sortBySpecificity(element, rules) { // comparing function that sorts CSSStyleRules according to specificity of their `selectorText` function compareSpecificity (a, b) { return getSpecificityScore(element, b.selectorText) - getSpecificityScore(element, a.selectorText); } return rules.sort(compareSpecificity); } // Find correct matchesSelector impl function matchesSelector(el, selector) { var matcher = el.matchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector || el.msMatchesSelector; return matcher.call(el, selector); } //TODO: not supporting 2nd argument for selecting pseudo elements //TODO: not supporting 3rd argument for checking author style sheets only window.getMatchedCSSRules = function (element /*, pseudo, author_only*/) { var style_sheets, sheet, sheet_media, rules, rule, result = []; // get stylesheets and convert to a regular Array style_sheets = toArray(window.document.styleSheets); // assuming the browser hands us stylesheets in order of appearance // we iterate them from the beginning to follow proper cascade order while (sheet = style_sheets.shift()) { // get the style rules of this sheet rules = getSheetRules(sheet); // loop the rules in order of appearance while (rule = rules.shift()) { // if this is an @import rule if (rule.styleSheet) { // insert the imported stylesheet's rules at the beginning of this stylesheet's rules rules = getSheetRules(rule.styleSheet).concat(rules); // and skip this rule continue; } // if there's no stylesheet attribute BUT there IS a media attribute it's a media rule else if (rule.media) { // insert the contained rules of this media rule to the beginning of this stylesheet's rules rules = getSheetRules(rule).concat(rules); // and skip it continue } // check if this element matches this rule's selector if (matchesSelector(element, rule.selectorText)) { // push the rule to the results set result.push(rule); } } } // sort according to specificity return sortBySpecificity(element, result); }; }
Correction de bugs
= match
→+= match
return re ? re.length : 0;
→return matches ? matches.length : 0;
_matchesSelector(element, selector)
→matchesSelector(element, selector)
matcher(selector)
→matcher.call(el, selector)
la source
Voici une version de la réponse de SB qui renvoie également des règles de correspondance dans les requêtes multimédias correspondantes. J'ai supprimé la
*.rules || *.cssRules
coalescence et l'.matches
outil de recherche d'implémentation; ajoutez un polyfill ou rajoutez ces lignes si vous en avez besoin.Cette version renvoie également les
CSSStyleRule
objets plutôt que le texte de la règle. Je pense que c'est un peu plus utile, car les spécificités des règles peuvent être plus facilement explorées par programme de cette façon.Café:
getMatchedCSSRules = (element) -> sheets = document.styleSheets matching = [] loopRules = (rules) -> for rule in rules if rule instanceof CSSMediaRule if window.matchMedia(rule.conditionText).matches loopRules rule.cssRules else if rule instanceof CSSStyleRule if element.matches rule.selectorText matching.push rule return loopRules sheet.cssRules for sheet in sheets return matching
JS:
function getMatchedCSSRules(element) { var i, len, matching = [], sheets = document.styleSheets; function loopRules(rules) { var i, len, rule; for (i = 0, len = rules.length; i < len; i++) { rule = rules[i]; if (rule instanceof CSSMediaRule) { if (window.matchMedia(rule.conditionText).matches) { loopRules(rule.cssRules); } } else if (rule instanceof CSSStyleRule) { if (element.matches(rule.selectorText)) { matching.push(rule); } } } }; for (i = 0, len = sheets.length; i < len; i++) { loopRules(sheets[i].cssRules); } return matching; }
la source
element
?cloneNode(true)
fonctionnalités, mais avec un style également cloné en profondeur.Voici ma version de la
getMatchedCSSRules
fonction qui prend en charge la@media
requête.const getMatchedCSSRules = (el) => { let rules = [...document.styleSheets] rules = rules.filter(({ href }) => !href) rules = rules.map((sheet) => [...(sheet.cssRules || sheet.rules || [])].map((rule) => { if (rule instanceof CSSStyleRule) { return [rule] } else if (rule instanceof CSSMediaRule && window.matchMedia(rule.conditionText)) { return [...rule.cssRules] } return [] })) rules = rules.reduce((acc, rules) => acc.concat(...rules), []) rules = rules.filter((rule) => el.matches(rule.selectorText)) rules = rules.map(({ style }) => style) return rules }
la source
var GetMatchedCSSRules = (elem, css = document.styleSheets) => Array.from(css) .map(s => Array.from(s.cssRules).filter(r => elem.matches(r.selectorText))) .reduce((a,b) => a.concat(b)); function Go(paragraph, print) { var rules = GetMatchedCSSRules(paragraph); PrintInfo: print.value += "Rule 1: " + rules[0].cssText + "\n"; print.value += "Rule 2: " + rules[1].cssText + "\n\n"; print.value += rules[0].parentStyleSheet.ownerNode.outerHTML; } Go(document.getElementById("description"), document.getElementById("print"));
p {color: red;} #description {font-size: 20px;}
<p id="description">Lorem ipsum</p> <textarea id="print" cols="50" rows="12"></textarea>
la source
Assurant IE9 +, j'ai écrit une fonction qui calcule le CSS pour l'élément demandé et ses enfants, et donne la possibilité de l'enregistrer dans un nouveau nom de classe si nécessaire dans l'extrait ci-dessous.
/** * @function getElementStyles * * Computes all CSS for requested HTMLElement and its child nodes and applies to dummy class * * @param {HTMLElement} element * @param {string} className (optional) * @param {string} extras (optional) * @return {string} CSS Styles */ function getElementStyles(element, className, addOnCSS) { if (element.nodeType !== 1) { return; } var styles = ''; var children = element.getElementsByTagName('*'); className = className || '.' + element.className.replace(/^| /g, '.'); addOnCSS = addOnCSS || ''; styles += className + '{' + (window.getComputedStyle(element, null).cssText + addOnCSS) + '}'; for (var j = 0; j < children.length; j++) { if (children[j].className) { var childClassName = '.' + children[j].className.replace(/^| /g, '.'); styles += ' ' + className + '>' + childClassName + '{' + window.getComputedStyle(children[j], null).cssText + '}'; } } return styles; }
Usage
getElementStyles(document.getElementByClassName('.my-class'), '.dummy-class', 'width:100%;opaity:0.5;transform:scale(1.5);');
la source
computeStyles
sous-programme par simplementel => getComputedStyle(el).cssText
. Preuve: violon . 2.'.' + element.className
est une construction défectueuse car elle suppose l'existence d'un nom de classe. La construction valide estelement.className.replace(/^| /g, '.')
. 3. Votre fonction ignore la possibilité d'autres sélecteurs CSS que de simples classes. 4. Votre récursivité est arbitrairement limitée à un niveau (enfants mais pas petits-enfants). 5. Utilisation: il n'y a pasgetElementByClassName
, seulementgetElementsByClassName
(renvoie un tableau).Je pense que la réponse de SB devrait être acceptée à ce stade, mais elle n'est pas exacte. Il est mentionné à quelques reprises que certaines règles pourraient être manquées. Face à cela, j'ai décidé d'utiliser document.querySelectorAll au lieu de element.matches. La seule chose est que vous auriez besoin d'une sorte d'identification unique des éléments pour le comparer à celui que vous recherchez. Dans la plupart des cas, je pense que cela est réalisable en définissant son identifiant pour avoir une valeur unique. C'est ainsi que vous pouvez identifier l'élément correspondant comme étant le vôtre. Si vous pouvez penser à un moyen général de faire correspondre le résultat de document.querySelectorAll à l'élément que vous recherchez, ce serait essentiellement un polyfill complet de getMatchedCSSRules.
J'ai vérifié les performances de document.querySelectorAll car il est probablement plus lent que element.matches mais dans la plupart des cas, cela ne devrait pas poser de problème. Je vois que cela prend environ 0,001 milliseconde.
J'ai également trouvé la bibliothèque CSSUtilities qui annonce qu'elle peut le faire mais je pense qu'elle est ancienne et qu'elle n'a pas été mise à jour depuis un moment. En regardant son code source, cela me fait penser qu'il peut y avoir des cas où il manque.
la source