Banyak alat / API menyediakan cara memilih elemen dari kelas atau ID tertentu. Ada juga kemungkinan untuk memeriksa stylesheet mentah yang dimuat oleh browser.
Namun, agar browser merender elemen, mereka akan mengkompilasi semua aturan CSS (mungkin dari file stylesheet yang berbeda) dan menerapkannya ke elemen tersebut. Inilah yang Anda lihat dengan Firebug atau WebKit Inspector - pohon pewarisan CSS lengkap untuk sebuah elemen.
Bagaimana saya dapat mereproduksi fitur ini dalam JavaScript murni tanpa memerlukan plugin browser tambahan?
Mungkin sebuah contoh dapat memberikan klarifikasi tentang apa yang saya cari:
<style type="text/css">
p { color :red; }
#description { font-size: 20px; }
</style>
<p id="description">Lorem ipsum</p>
Di sini elemen p # description memiliki dua aturan CSS yang diterapkan: warna merah dan ukuran font 20 px.
Saya ingin menemukan sumber dari mana aturan CSS yang dihitung ini berasal (warna berasal dari aturan p dan seterusnya).
sumber
Jawaban:
Karena pertanyaan ini saat ini tidak memiliki jawaban ringan (non-perpustakaan) yang kompatibel dengan lintas browser, saya akan mencoba memberikannya:
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/
Memanggil
css(document.getElementById('elementId'))
akan mengembalikan larik dengan elemen untuk setiap aturan CSS yang cocok dengan elemen yang diteruskan. Jika Anda ingin mengetahui informasi yang lebih spesifik tentang setiap aturan, lihat dokumentasi objek CSSRule .sumber
a.matches
didefinisikan di baris ini:a.matches = a.matches || a.webkitMatchesSelector || a.mozMatchesSelector || a.msMatchesSelector || a.oMatchesSelector
. Artinya, jika sudah ada metode "kecocokan" (standar) untuk Node DOM, ia akan menggunakan yang itu, jika tidak mencoba menggunakan yang spesifik Webkit (webkitMatchesSelector), lalu Mozilla, Microsoft dan Opera. Anda dapat membacanya lebih lanjut di sini: developer.mozilla.org/en/docs/Web/API/Element/matchesif (a.matches(rules[r].selectorText))
kondisi penjagaan.css()
setiap elemen induk.EDIT: Jawaban ini sekarang tidak digunakan lagi dan tidak lagi berfungsi di Chrome 64+ . Meninggalkan konteks sejarah. Faktanya, laporan bug menautkan kembali ke pertanyaan ini untuk solusi alternatif untuk menggunakan ini.
Sepertinya saya berhasil menjawab pertanyaan saya sendiri setelah satu jam penelitian.
Sesederhana ini:
window.getMatchedCSSRules(document.getElementById("description"))
(Bekerja di WebKit / Chrome, mungkin orang lain juga)
sumber
Versi pendek 12 April 2017
Challenger muncul.
var getMatchedCSSRules = (el, css = el.ownerDocument.styleSheets) => [].concat(...[...css].map(s => [...s.cssRules||[]])) /* 1 */ .filter(r => el.matches(r.selectorText)); /* 2 */
Garis
/* 1 */
membangun larik datar dari semua aturan.Garis
/* 2 */
membuang aturan yang tidak cocok.Berdasarkan fungsinya
css(el)
@SB di halaman yang sama.Contoh 1
var div = iframedoc.querySelector("#myelement"); var rules = getMatchedCSSRules(div, iframedoc.styleSheets); console.log(rules[0].parentStyleSheet.ownerNode, rules[0].cssText);
Contoh 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>
Kekurangan
@import
,@media
.Mungkin saya akan membahas kekurangan ini suatu hari nanti.
Versi panjang 12 Agustus 2018
Berikut implementasi yang jauh lebih komprehensif yang diambil dari halaman GitHub seseorang (bercabang dari kode asli ini , melalui Bugzilla ). Ditulis untuk Gecko dan IE, tetapi dikabarkan bekerja juga dengan Blink.
4 Mei 2017: Kalkulator kekhususan memiliki bug kritis yang sekarang telah saya perbaiki. (Saya tidak dapat memberi tahu penulis karena saya tidak memiliki akun GitHub.)
12 Agustus 2018: Pembaruan Chrome terbaru tampaknya telah memisahkan cakupan objek (
this
) dari metode yang ditetapkan ke variabel independen. Oleh karena itu, permintaanmatcher(selector)
berhenti bekerja. Menggantinya denganmatcher.call(el, selector)
telah menyelesaikannya.// 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); }; }
Memperbaiki bug
= match
→+= match
return re ? re.length : 0;
→return matches ? matches.length : 0;
_matchesSelector(element, selector)
→matchesSelector(element, selector)
matcher(selector)
→matcher.call(el, selector)
sumber
Lihatlah perpustakaan ini, yang melakukan apa yang diminta: http://www.brothercake.com/site/resources/scripts/cssutilities/
Ia bekerja di semua browser modern langsung kembali ke IE6, dapat memberi Anda aturan dan koleksi properti seperti Firebug (sebenarnya lebih akurat daripada Firebug), dan juga dapat menghitung kekhususan relatif atau absolut dari aturan apa pun. Satu-satunya peringatan adalah bahwa, meskipun memahami jenis media statis, ia tidak memahami kueri media.
sumber
Berikut adalah versi jawaban SB yang juga mengembalikan aturan yang cocok dalam kueri media yang cocok. Saya telah menghapus
*.rules || *.cssRules
penggabungan dan.matches
pencari implementasi; tambahkan polyfill atau tambahkan kembali baris tersebut jika Anda membutuhkannya.Versi ini juga mengembalikan
CSSStyleRule
objek daripada teks aturan. Saya rasa ini sedikit lebih berguna, karena spesifikasi aturan dapat lebih mudah diperiksa secara terprogram dengan cara ini.Kopi:
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; }
sumber
element
juga?cloneNode(true)
fungsionalitas tetapi dengan gaya kloning yang dalam juga.Ini adalah versi saya dari
getMatchedCSSRules
fungsi yang mendukung@media
kueri.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 }
sumber
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>
sumber
Memastikan IE9 +, saya menulis fungsi yang menghitung CSS untuk elemen yang diminta dan turunannya, dan memberikan kemungkinan untuk menyimpannya ke className baru jika diperlukan dalam cuplikan di bawah.
/** * @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; }
Pemakaian
getElementStyles(document.getElementByClassName('.my-class'), '.dummy-class', 'width:100%;opaity:0.5;transform:scale(1.5);');
sumber
computeStyles
subrutin hanya denganel => getComputedStyle(el).cssText
. Bukti: biola . 2.'.' + element.className
merupakan konstruksi yang salah karena mengasumsikan keberadaan satu nama kelas. Konstruksi yang valid adalahelement.className.replace(/^| /g, '.')
. 3. Fungsi Anda mengabaikan kemungkinan pemilih CSS lain selain kelas. 4. Rekursi Anda secara sewenang-wenang terbatas pada satu tingkat (anak-anak tetapi bukan cucu). 5. Penggunaan: tidak adagetElementByClassName
, hanyagetElementsByClassName
(mengembalikan array).Saya pikir jawaban dari SB seharusnya yang diterima pada saat ini tetapi tidak tepat. Disebutkan beberapa kali bahwa akan ada beberapa aturan yang mungkin terlewat. Menghadapi hal itu, saya memutuskan untuk menggunakan document.querySelectorAll daripada element.matches. Satu-satunya hal adalah Anda memerlukan semacam identifikasi unik dari elemen untuk membandingkannya dengan yang Anda cari. Dalam kebanyakan kasus saya pikir itu dapat dicapai dengan mengatur id-nya agar memiliki nilai yang unik. Begitulah cara Anda mengidentifikasi elemen yang cocok menjadi milik Anda. Jika Anda bisa memikirkan cara umum untuk mencocokkan hasil document.querySelectorAll dengan elemen yang Anda cari, itu pada dasarnya adalah polyfill lengkap getMatchedCSSRules.
Saya memeriksa kinerja untuk document.querySelectorAll karena mungkin lebih lambat dari element.matches tetapi dalam banyak kasus seharusnya tidak menjadi masalah. Saya melihat bahwa dibutuhkan sekitar 0,001 milidetik.
Saya juga menemukan pustaka CSSUtilities yang mengiklankan bahwa ia dapat melakukan ini tetapi saya merasa ini sudah tua dan belum diperbarui dalam beberapa saat. Melihat kode sumbernya, itu membuat saya berpikir mungkin ada kasus yang terlewat.
sumber