JavaScriptでの要素のスタイル操作で !important を使う方法

#はじめに

JavaScriprで要素のスタイルを普通に変更しようとしても、元々のCSSで `!important` 指定されている場合変更できないという問題があります。
`!important` を使うこと自体ナンセンスな部分がありますが、仕事で使う機会があったのでいろいろな方法を挙げてみました。

それぞれの方法を見てみます。

#いろいろな方法

それぞれのメリット・デメリットも含めて。

以下のようなHTMLがあるとします。（いろいろ省略してます）

```html:HTML
<style>
#target {
  background-color: #f00 !important;
}
</style>

<div id="target" style="width: 100px; height: 100px;"></div>
```

この `#target` に対して背景色を変えてみます。

##`element.style.*`

よく使う書き方ですが、この方法では `!important` を指定することはできません。

```js:JavaScript
var target = document.getElementById('target');

target.style.backgroundColor = '#00f';
// styleとして適用されるがこれでは優先的に下

target.style.backgroundColor = '#00f !important';
// エラーにはならないが変わらない
```

そもそも不可能なのでメリットもデメリットもないですね。

##jQueryの `.css()`

万能なjQueryですが、この方法も同じように `!important` を指定することはできません。

```js:JavaScript
var $target = $('#target');

$target.css('background-color', '#00f');
// styleとして適用されるがこれでは優先的に下

$target.css('background-color', '#00f !important');
// エラーにはならないが変わらない
```

これも不可能なのでメリットもデメリットもありません。

##`element.style.cssText`

`element.style.cssText` は、要素のstyle属性で指定されているスタイルが文字列として取得でき、更に変更できるプロパティです。
これに代入して変更する方法です。

```js:JavaScript
var
  target = document.getElementById('target'),
  cssText = target.style.cssText;

console.log(cssText);  // "width: 100px; height: 100px;"

target.style.cssText = cssText + 'background-color: #00f !important;';
// 適用された！

// これでもいい
// target.style.cssText += 'background-color: #00f !important;';
```

もし既に `cssText` に `background-color` がある場合はスタイルが上書きされます。

###メリット

- CSSそのままの書式で書くことができる
- 複数のスタイルを一度に書くことができる

###デメリット

- 既にstyle属性で指定されている場合、それらが上書きされないようにしなければならない
- プロパティ名や値を変数で持ちたい場合などに文字列結合が面倒くさい

##`element.setAttribute()`

`element.setAttribute()` は、要素の属性の値をセットするメソッドですが、これをstyle属性に対して使用する方法です。

```js:JavaScript
var
  target = document.getElementById('target'),
  style = target.getAttribute('style');

console.log(style);  // "width: 100px; height: 100px;"

target.setAttribute('style', style + 'background-color: #00f !important;');
// 適用された！
```

もし既にstyle属性に `background-color` がある場合はスタイルが上書きされます。

###メリット

- CSSそのままの書式で書くことができる
- 複数のスタイルを一度に書くことができる

###デメリット

- 既にstyle属性で指定されている場合、それらが上書きされないようにしなければならない
- プロパティ名や値を変数で持ちたい場合などに文字列結合が面倒くさい
- 書式が間違っていてもそのままstyle属性の値として保持してしまう

##`element.style.setProperty()`

`element.style.setProperty()` はチェインケースでスタイルを指定できるメソッドです。

```js:JavaScript
var target = document.getElementById('target');

target.style.setProperty('background-color', '#00f', 'important');
// 適用された！
```

第3引数を `'important'` とすることで、`!important` が適用されます。（頭の感嘆符 `!` は不要です）
もし、必要でない場合は第3引数を省略したり空文字や `null` を指定します。

###メリット

- 見た目がすっきりしている
- キャメルケースではなくチェインケースで書けるので、書き間違いが起こらない
- 引数ごとに分かれているので、プロパティ名や値を変数で持っている場合も扱いやすい

###デメリット

- まとめて指定できないので、適用したい分だけメソッドを書く必要がある

##style要素を生成する

もう面倒だからstyle要素を作ってしまえという方法です。

```js:JavaScript
var style = document.createElement('style');

document.head.appendChild(style);

style.textContent = '#target {background-color: #00f !important;}';
// 適用された！
```

###メリット

- 要素を直接いじらずにスタイルの操作ができる
- CSSそのままの書式で書くことができる
- 複数のスタイルを一度に書くことができる
- JavaScriptでは取得できない擬似要素に対してスタイルを指定することができる

###デメリット

- 無駄なstyle要素が増えてしまう
- プロパティ名や値を変数で持ちたい場合などに文字列結合が面倒くさい
- ECMAScript 2015 のテンプレートリテラルが使用できない場合、複数行にわたる書き方が面倒くさい

##`CSSStyleSheet.insertRule()`

style要素の `CSSStyleSheet` オブジェクトの `CSSStyleSheet.insertRule()` というメソッドを使う方法です。

```js:JavaScript
// style属性が存在する場合はそれを利用する
var
  doc = document,
  style = doc.getElementsByTagName('style')[0] || doc.head.appendChild(doc.createElement('style')),
  styleSheet = style.sheet;

styleSheet.insertRule('#target {background-color: #00f !important;}', styleSheet.cssRules.length);
// 適用された！

// ちなみに、ひとつのルール毎に使うメソッドなので、複数のルールを一度に指定できない
// styleSheet.insertRule('#foo {prop: value;} #bar {prop: value;}', styleSheet.cssRules.length);
```

`CSSStyleSheet` オブジェクトとは、style要素が内部で持っているCSSのルールをまとめたオブジェクトです。

###メリット

- 要素を直接いじらずにスタイルの操作ができる
- CSSそのままの書式で書くことができる
- ひとつのルールに複数のスタイルを一度に書くことができる
- JavaScriptでは取得できない擬似要素に対してスタイルを指定することができる

###デメリット

- 書き方が少し複雑
- 無駄なstyle要素が増えてしまう
- プロパティ名や値を変数で持ちたい場合などに文字列結合が面倒くさい
- ECMAScript 2015 のテンプレートリテラルが使用できない場合、複数行にわたる書き方が面倒くさい

#おわりに

メリット・デメリットは他にもこんなのがあるよという場合は遠慮なく言ってください。