如何使用XUL,SDK或WebExtensions技术从Firefox附加组件中的JavaScript动态修改CSS规则集(例如使用类选择器)?

时间:2016-05-28 07:56:34

标签: javascript css dom firefox-addon xul

如何使用XUL,SDK或WebExtensions技术从Firefox附加组件中的JavaScript动态修改CSS规则集(例如使用类选择器)?试图支持Firefox 29.0b1到49.0a1。

我想解决的问题(来自Firefox附加组件中的XUL文档,有时与网页不同):

file.xul: (此行位于顶部)

<?xml-stylesheet href="chrome://{GUID}/skin/text.css" type="text/css"?>

text.css: (文件中的相关课程)

.myTextClass {
    max-width: 800px;
}

code:

// Modify the class selector rule set to add another rule,
// i.e. font-family: DejaVu Sans Mono

// Later on, after user input, change or remove this font in this one class

这可能是#20927075的副本,但没有令人满意的答案,但我也对SDK或WebExtensions技术感兴趣,以确保我想要添加到现有的功能基于XUL的附加组件可以使用较新的API实现。

我想实现一个基本的“字体选择器”UI功能,并让它将新字体应用于该类。我有一个简单的CSS规则集,带有一个类选择器,它定义了一个带有一个规则的类(现在,但可能有其他手动/静态添加的规则集),我想添加另一个规则,并且有加载项的界面更新。

我所知道的选项,但都显得过于笨拙。

1)document.getElementById,手动设置style属性。我为演示区做这个,没关系。

2)与上面类似,但是getElementByClassName。但是,这不是一个类!这是使用一个类来查找节点,我更改了每个元素的属性,无论是样式还是类。这似乎很慢。我可以使用每种可能的字体创建一个庞大的CSS文件,然后更改类属性,但这是愚蠢的。比仅更改样式属性更糟糕。

3)有样式表服务,但我不知道它是否仍然存在或将被淘汰或删除(它是基于XPCOM的?),它不是Services.jsm的一部分,它是一个非常钝的工具当我只想从一个类定义中修改一个规则时,它只能加载或卸载整个样式表。

4)有jQuery,但是没有。就是不行。甚至不打算提出这个建议。小插件的开销太大,不知道该技术是否适用于XUL接口代码。

5)像document.styleSheets这样的东西似乎没错,但是没有得到我想要的东西(除非我弄错了?)。一切似乎都是只读的。更多信息如下。

6)通过摆弄document.getElementsByTagName('head')手动破坏整个样式表,这再次破坏整个css表而不是一个类的一个规则。

如果确实没有其他选择,我可能必须使用上面的方法2。但我希望有一个选项允许使用JavaScript在XUL中对CSS进行细粒度控制。

我正在尝试,了解如何执行此操作:

for ( i = 0; i < document.styleSheets.length; i++ ) {
    styleSheet = document.styleSheets[ i ];

    console.log( 'style sheet: ' + i + ' ' + styleSheet.href );

    if ( styleSheet.href === 'chrome://{GUID}/skin/text.css' ) {
        for ( j = 0; j < styleSheet.cssRules.length; j++ ) {
            styleRule = styleSheet.cssRules[ j ];
            console.log( 'style sheet: ' + i + ' ' + 'styleRule: ' + styleRule.cssText );
        }
    }
}

在此编辑过程中,我现在获取代码以显示定义类的规则,而昨晚我收到了无效的属性错误。我的术语混淆了,认为规则是课堂内的项目。但无论如何,从那时起,我不确定如何继续。

以上代码(在file.js中引用file.xul <script>标记)输出与此类似的console.log(在Firefox 29.0b1中):

"style sheet: 0 chrome://git-addonbar/skin/addonbar.css"                              file.js:189
09:49:47.101 "style sheet: 1 chrome://browser/content/browser.css"                    file.js:189
09:49:47.101 "style sheet: 2 chrome://browser/content/places/places.css"              file.js:189
09:49:47.101 "style sheet: 3 chrome://browser/skin/devtools/common.css"               file.js:189
09:49:47.101 "style sheet: 4 chrome://browser/skin/customizableui/panelUIOverlay.css" file.js:189
09:49:47.101 "style sheet: 5 chrome://browser/skin/browser.css"                       file.js:189
09:49:47.101 "style sheet: 6 chrome://browser/skin/browser-lightweightTheme.css"      file.js:189
09:49:47.101 "style sheet: 7 chrome://global/skin/global.css"                         file.js:189
09:49:47.101 "style sheet: 8 chrome://{GUID}/skin/text.css"                           file.js:189
09:49:47.102 "style sheet: 8 styleRule: .myTextClass { max-width: 800px; }"           file.js:194
09:49:47.102 "style sheet: 9 null"                                                    file.js:189

3 个答案:

答案 0 :(得分:2)

  

5)像document.styleSheets这样的东西似乎没有得到我想要的东西(除非我弄错了?)。一切似乎都是只读的。

这是正确的选择。虽然styleSheets属性是只读的(意味着您不能像这样分配:document.styleSheets = val),但您获得的样式表对象确实允许修改。

由于您只关心一个(现代!)浏览器,因此以跨浏览器的方式比Changing a CSS rule-set from Javascript更容易。一个简单的例子:

function changeCSS() {
  let myClassRule = document.styleSheets[0].cssRules[0];
  if (myClassRule.style.color == "red")
    myClassRule.style.color = "blue";
  else
    myClassRule.style.color = "red";
}
.my-class {
  color: red;
}
<p class="my-class">First paragraph</p>
<p class="my-class">Second paragraph</p>
<button onclick="changeCSS()">Change the stylesheet!</button>

您必须对其进行扩展才能找到您关注的样式表和规则 - 上面链接的问题有a minimal example of doing that

答案 1 :(得分:2)

要回答我自己的问题,这是最终工作并解决了我的问题的代码。关键信息如下:

document.styleSheets[ i ].href
document.styleSheets[ i ].cssRules[ j ].selectorText
document.styleSheets[ i ].cssRules[ j ].style.fontFamily

这一切都始于document.styleSheets类似数组的对象(类似于数组,因为它实际上不是一个JavaScript数组),其余的都是简单的DOM。可以进行通用功能。 href可以是可选的,具体取决于你是否知道加载外部工作表的位置,并且你知道你可以安全地担心只有一个独特的选择器,那么你可以避免搜索Firefox加载的~10张其他工作表。这是最简单的用例。

// Load font and apply style sheet class

// Am using the .href to find the sheet faster because location is known and rule is unique
var myCSShref = 'chrome://{GUID}/skin/text.css';
var mySelector = '.myTextClass';

// fonts stored as a string in a user pref, later split to array inline
// fonts could be stored as array during runtime, remove splits accordingly
// fonts get appended to a XUL menulist element
// list index 0 is for default, 1+ for fonts below
var fonts = 'monospace|Anonymous Pro|Apple Menlo|Bitstream Vera Sans Mono|Consolas|DejaVu Sans Mono|Droid Sans Mono|Everson Mono|Liberation Mono|Lucida Console|Source Code Pro';
var font_index = 2; // Anonymous Pro, the 2 is pulled from a user pref

// for loop vars
var i, styleSheet, j, cssRule, oldFontFamily, newFontFamily;

// console.logs for demonstration purposes only

// The following code only needed to be run once for me,
// but it can easily be changed to a reusable function.

if ( document.styleSheets ) {
    for ( i = 0; i < document.styleSheets.length; i++ ) {
        styleSheet = document.styleSheets[ i ];

        console.log( 'style sheet: ' + i + ' ' + styleSheet.href );

        if ( styleSheet.href === myCSShref ) {
            for ( j = 0; j < styleSheet.cssRules.length; j++ ) {
                cssRule = styleSheet.cssRules[ j ];
                console.log( 'style sheet: ' + i + ' cssRule.selectorText: ' + cssRule.selectorText );

                if ( cssRule && cssRule.selectorText === mySelector ) {
                    oldFontFamily = ( cssRule.style.fontFamily ) ? cssRule.style.fontFamily : '';
                    // font_index 0 is the menu default option, so we have 1 extra
                    newFontFamily = ( font_index === 0 ) ? 'inherit' : fonts.split( '|' )[ font_index - 1 ];
                    console.log( 'style sheet: ' + i + ' cssRule.style.fontFamily: ' + oldFontFamily + ' (before)' );
                    console.log( 'style sheet: ' + i + ' cssRule.style.fontFamily: ' + newFontFamily + ' (after)' );
                    cssRule.style.fontFamily = newFontFamily;
                }
            }
        }
    }
}

答案 2 :(得分:0)

  

3)有样式表服务,但我不知道它是否仍然存在或将被淘汰或删除(它是基于XPCOM的?),它不是Services.jsm的一部分,它是一个非常钝的工具当我只想从一个类定义中修改一个规则时,它只能加载或卸载整个样式表。

目前,其他插件API是表单服务或windowutils.loadSheet的抽象。当然,mozilla最终可能会在通过其他方式保持抽象的同时将其删除。但是,现在看来,未来还有一年的时间。

addon-sdk提供了使用page-mod模块

加载样式的选项

Webextensions通过tabs.insertCSS强制支持或manifest

以声明方式支持

但这些都是针对内容框架的。如果你想修改浏览器chrome本身,你无论如何都必须使用xpcom API,因为没有针对它的专用插件API。