2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[小ネタ] knockout.jsのattrバインディングでreadonly属性を設定する

Posted at

HTMLのreadonly属性はちょっと変わった属性で、名前と値の組ではなくて属性名だけで指定します1。この属性をattrバインディングを使って設定しようとすると意外と難しかったりします。どう難しいか順を追ってみていきましょう。

まず、attrバインディングを使ってreadonly属性を変更する場合は、以下のように記述することになります。

<input type="text" data-bind="attr: { readonly: readonlyAttr }">

readonlyAttrは属性値を返すobservableなプロパティです。が、これはいったい何を返せば良いのでしょうか。値など要らないわけですが、何かを返さないとattrバインディングが満足してくれません。

ここでHTMLの仕様を記憶からひねり出します。実は属性名のみの指定はreadonly="readonly"の略記です。ということはつまり、readonlyAttrは以下のようにすればいいはずです。

self.readonlyAttr = ko.computed(function() {
    return self.isReadonly() ? 'readonly' : '';
});

が、うまくいきません。isReadonly()が偽の時もreadonlyが有効になってしまいます。なぜかというとreadonly=""readonly="readonly"は同じ意味に解釈されるからです2。これはつまりreadonlyを無効にするためには属性名ごと削除しないといけないということです。

なんだか急に難易度が上がりました。attrバインディングのパラメータごと切り替えるみたいなことが果たしてできるのか、あるいは独自のバインディングを作ったほうが色々早いのではないか……

ここで独自バインディング実装の参考という名目でknockout.jsのソースコード探索に逃げてみましょう。するとattrバインディングになにやら意味深なコメントがあるのが見つかります。

knockout/src/binding/defaultBindings/attr.js
            // To cover cases like "attr: { checked:someProp }", we want to remove the attribute entirely
            // when someProp is a "no value"-like value (strictly null, false, or undefined)
            // (because the absence of the "checked" attr is how to mark an element as not checked, etc.)
            var toRemove = (attrValue === false) || (attrValue === null) || (attrValue === undefined);
            if (toRemove)
                element.removeAttribute(attrName);

なんと、属性値としてnullfalseundefinedを指定すると属性を削除してくれるようです。まさにこんな時のための機能があるではありませんか3

ということで、このように指定すれば目指した挙動になりました。めでたしめでたし。

self.readonlyAttr = ko.computed(function() {
    return self.isReadonly() ? 'readonly' : null;
});

ちなみにisReadonlyをそのまま使っても動作するようですが、その場合はtrueの時にreadonly="true"になってしまいます。属性値がtrueの場合の挙動はHTMLの仕様からは見つけられませんでしたが、概ねreadonly属性が有効だと解釈されるようです。

  1. 同じような属性としてcheckeddisabledなどがありますが、これらには対応するバインディングが用意されているので困ることはありません。

  2. HTML5: 2.4.2 Boolean attributes

  3. この仕様はドキュメントに記載されていません。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?