網站搜尋

javascript - getcomputedstyle 只取與預設不同的部分


我目前的程式碼會取得整個 CSS,包括預設的部分。我想要的是只取得與預設不同的 CSS。

function baba() {
  addEventListener("mouseover", function() {
    var elem = document.getElementById("ex");
    cssObj = window.getComputedStyle(elem, null)
    var txt = "";

    for (i = 0; i < cssObj.length; i++) {
      cssObjProp = cssObj.item(i)
      txt += cssObjProp + " = " + cssObj.getPropertyValue(cssObjProp) + "<br>";

      document.getElementById("empty").innerHTML = txt;
    }
  })
}
<p id="ex" onclick="baba()">Hello World</p>
<h1>Hello World</h1>
<p id="empty"></p>

答案#1

好吧,這就是我會處理它的方式。注意:我只是花五分鐘在控制台裡匆匆完成這個;我相信有更有效的方法來處理它,但對於概念驗證來說,這應該足夠讓你開始了。

需求分析

真的(除非有更特定的邊緣案例應用,無論如何),你所問的其實是「元素 <XXX> 的目前計算樣式與相同類型的原始物件在相同上下文中有何不同?」. 問它與「預設」有何不同是無意義的,因為「預設」必然會受到該上下文的影響(不同意嗎?等一下,我會解釋的)。

因此,我們真正需要檢查的是一個 <XXX>,它沒有應用到你的目標物件的效果(其 DOM 位置、類別、id、屬性、前置元素等的結果)。好消息是:我們完全可以偽造它!看看這個:

設定

首先,最重要的是,我們先取得我們的物件。我知道這最好用函數來執行,但為了說明起見,請和我一起操作。我們來挑一個你可以立即看到結果的物件。讓我們看看……頂部的搜尋欄如何?按下 F12 打開你的控制台,你就會看到它的名稱是 'q'。這樣就可以了。

// Get the StackOverflow Search field from the top of this page.
var targetDOMElement = document.querySelector('[name="q"]');
// Now, let's get its current style snapshot.
var targetObjsStyles = window.getComputedStyle(targetDOMElement);
// ... and vomit it out to our console, just so we know what we're dealing with.
console.log('ORIGINAL SET (' + Object.keys(targetObjsStyles).length + ' rules):',targetObjsStyles);

太好了!現在我們有了我們的來源物件(如果你願意叫它「<XXX>」)。

控制

接下來,我們需要一些東西來與它作比較。現在,作為一個被正統科學家教育出的乖小孩,在我心中,那就是一個對照。幸運的是,我們對我們的源物件知道很多,所以讓我們製造一個:

// Create a new element of the same type (e.g. tagName) as our target
var tempCopyOfTarget = document.createElement(targetDOMElement.tagName);
// Insert it into the context AT THE BEGINNING of the page. Both bits here are important:
// if we create it within a documentFragment (try it) literally every property will 
// be flagged as unique. I suspect this has to do with the client's default 
// renderer, vs. the purity of a abstracted prototype, but I won't lie: I'm guessing.
// It MUST be at the start of the body to avoid silliness like
// body > .first-element ~ xxx { display:none; }
// CSS still won't let us target predecessors/ancestors, alas.
document.body.insertAdjacentElement('afterBegin', tempCopyOfTarget);
// Now  our new object shares our target's context, get ITS snapshot.
var basicElementsCSS = window.getComputedStyle(tempCopyOfTarget);
console.log('BASELINE (DUMMY OBJECT) SET (' + Object.keys(basicElementsCSS).length + ' rules):',basicElementsCSS);

繁重的工作

雖然我確信大多數人此刻已經明白我的意思,但讓我們把它完成。給定一個可測量的量和一個控制,檢查差異。

// Create an empty object to store any changes in.
var cleanSetOfStyles = {};
// Objectify our target's style snapshot, and iterate.
Object.entries(targetObjsStyles).forEach(p=>{
    // If a key-value pair exists that matches our control, ignore it. Otherwise,
    // tack it onto our clean object for later perusal.
    if(basicElementsCSS[p[0]] !== p[1]){ 
        cleanSetOfStyles[p[0]] = p[1];
    }
});

太棒了!幹得好!

結論

現在,假設我的假設是正確的,我們應該在我們的乾淨物件中看到一組屬性及其對應的值。這個列表的長度應該既非零,也不同於上述原始集合中的計數(你們中較細心的人會注意到,這些原始集合是相同的,因為當請求一個 getComputedStyles 集合時,瀏覽器會將所有可能的樣式值分配給一個物件)。

// Display our deltas
console.log('CLEAN SET (' + Object.keys(cleanSetOfStyles).length + ' rules):',cleanSetOfStyles);
// Oh, and always remember to clean up after you make a mess in the lab.
tempCopyOfTarget.remove()

這是什麼!?勝利!至少在我的環境中(必須考慮我的瀏覽器型號、版本、啟用的外掛、支援的功能、作業系統等等;你的情況可能會不同),我數到有116條規則仍然存在並作用於我們的目標物件。這些規則與我們從零開始創建的原始物件不同,我們只是讓瀏覽器短暫查看它的一瞬間而已。

注意事項

總是有陷阱,不是嗎?這不是一個萬無一失的系統。我立刻就能想到至少六種它會失敗的方式(:empty 修飾符,取決於你所在的作用範圍……[name="q"] ~ [name="q"] 規則,現在插入我們的假物件使得 apply 作用於目標對象……:first-of-type 不再適用……各種『糟糕情況』)。但是,我準備主張我能想到的所有情況都是邊緣案例,且本身是可管理的,只要有適當的前瞻性思考。

摘要

這裡是完整程式碼,如果你有興趣,可以直接複製貼上到控制台,且不含註解:

var targetDOMElement = document.querySelector('[name="q"]');
var targetObjsStyles = window.getComputedStyle(targetDOMElement);
console.log('ORIGINAL SET (' + Object.keys(targetObjsStyles).length + ' rules):',targetObjsStyles)

var tempCopyOfTarget = document.createElement(targetDOMElement.tagName);
document.body.insertAdjacentElement('afterBegin', tempCopyOfTarget);

var basicElementsCSS = window.getComputedStyle(tempCopyOfTarget);
console.log('BASELINE (DUMMY OBJECT) SET (' + Object.keys(basicElementsCSS).length + ' rules):',basicElementsCSS)

var cleanSetOfStyles = {};
Object.entries(targetObjsStyles).forEach(p=>{
    if(basicElementsCSS[p[0]] !== p[1]){ 
        cleanSetOfStyles[p[0]] = p[1];
    }

});
console.log('CLEAN SET (' + Object.keys(cleanSetOfStyles).length + ' rules):',cleanSetOfStyles);

tempCopyOfTarget.remove()

最後的說明:我知道這個問題已經有幾個月了,但除了「不行!你完蛋了!」之外,真的沒有人回答過它。萬一 @angels7 還需要修復方法,給你這個。否則,「嗨,各位遙遠未來的人們!」