2014年5月31日

IE9.js(ie7-js) を DHTML でも使用する方法

IE9.js(ie7-js) は便利だが、画面表示後に変更された HTML(いわゆる DHTML・動的 HTML)に対してスタイルが適用されないことがある。

理由は単純で、主に擬似要素などブラウザが対応していないスタイルシートを javascript により適用しているためである。
javascript によるスタイル適用は画面ロード時の 1 回だけになり、後から変更された HTML に対しては何もできない。

そこで、スタイルを再適用させるための関数 IE7.recalc() が存在する。
IE9.js + DHTML でスタイルシートが適用されない場合は試してみる価値あり。
主に擬似要素を使ってるスタイルで有効。

使用例


<html>
<head>
<style type="text/css">
div > div:first-child > span {
    background-color: cyan;
}
</style>
<!--[if lt IE 9]>
<script src="http://ie7-js.googlecode.com/svn/version/2.1(beta4)/IE9.js"></script>
<![endif]-->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
    var index = 0;
    $('#add_dynamic').click(function(){
        $('#dynamic').append('<div><span>' + (index++) + '</span></div>');
        IE7.recalc();
    });
});
</script>
</head>

<body>
<input type="button" id="add_dynamic" value="add" />
<div id="dynamic"></div>
</body>
</html>
※first-child は IE7・IE8 でも使用可能だが、IE9.js を使用すると javascript 適用のスタイルシートになる。

参考URL

2014年5月30日

IE7 テキストボックスの上下余白(margin)

IE でテキストボックス <input type="text" /> を縦方向に並べる際、テキストボックスに margin:0 を指定しても、IE のバージョンによっては上下に余白ができることがある。
特に IE7 において顕著である。
※横方向に並べる際は問題ない。

具体例

[CSS]

div {
    margin: 0;
    padding: 0;
    width: 120px;
    /* 見易さのためのオプション */
    background-color: #00ffff;
    text-align: center;
}
div input {
    margin: 0;
    width: 50px;
    border: 1px solid #000000;
}
[HTML]
※DOCTYPE を指定しないとバージョン間の差異はさらに増える

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- 中略 -->
<div>
<input type="text" /><input type="text" /><br/>
<input type="text" /><input type="text" />
</div>
[プレビュー]
IE11
IE8~10もほぼ同じ
IE11 textbox
IE7 IE7 textbox

解決手段

見た目どおり上下の余白を取り除くという発想で、<input> に "margin: -1px 0" を指定しただけでは、縦中央に余白が残る。
IE7 textbox minus margin

そこで、<div> に font-size:12px を指定してみると、縦中央の余白も無くなる。
(理由は不明、高さ関係の計算で font-size が必要とか?)
<input> に対してではなく、それを包括する要素に指定するのがポイント
IE7 textbox minus margin and font-size

[IE7 に対応した CSS]

div {
    margin: 0;
    padding: 0;
    width: 120px;
    font-size: 12px;
    /* 見易さのためのオプション */
    background-color: #00ffff;
    text-align: center;
}
div input {
    margin: 0;
    width: 50px;
    border: 1px solid #000000;
}
/* IE7 のみ適用 */
*+html div input {
    margin: -1px 0;
}

/* [おまけ] reset.css で記載する場合
 * ※属性セレクタを使用するため、IE9.js などが必要
 */
*+html input[type=text] {
    margin: -1px 0;
}

備考

<input> に padding:0 を指定すると、IE8 と IE9 でも上に余白ができるが、上記と同様に <div> に font-size を指定すれば無くなる。
クロスブラウザ対応で font-size の指定は意外と重要。

2014年5月5日

再帰的なラムダ式

あまり実用的ではないが、.NET でラムダ式で再帰を行う方法はいくつかある。
ここでは、階乗を再帰の例とする。

ちなみに、普通の再帰メソッド。

public static int Factorial(int x)
{
    return x > 1 ? x * Factorial(x - 1) : 1;
}

1. ラムダ式割り当て変数で再帰


Func<int, int> fac1 = null;
fac1 = x => x > 1 ? x * fac1(x-1) : 1;
ラムダ式内で、自身を割り当てる変数をメソッドとして使用する方法。
たぶん、一番シンプルな方法になる。
ただし、コンパイラの都合上、変数宣言と同時にラムダ式の代入はできない。
※「未割り当てのローカル変数 '~' が使用されました。」エラーが出る。

また、ひねくれた使い方をするとバグの原因になる。

Func<int, int> fac1 = null;
fac1 = x => x > 1 ? x * fac1(x-1) : 1;
var fac1copy = fac1;
Console.WriteLine(fac1(4));     // 24
Console.WriteLine(fac1copy(4)); // 24

fac1 = x => x * x;
Console.WriteLine(fac1(4));     // 16 : 4 * 4
Console.WriteLine(fac1copy(4)); // 36 : 4 * fac1(3) = 4 * (3 * 3)

// fac1copy は最初の fac1 の内容から変わってないが、再帰として fac1 を使っているため、
// fac1 を変更すると影響を受けてしまう。

2. リフレクションで再帰


Func<int, int> fac2 = x =>
    x > 1 ? x * (int)MethodBase.GetCurrentMethod().Invoke(null, new object[]{x-1}) : 1;
リフレクションにより、自身を呼び出す方法。
javascript の arguments.callee (今や非推奨)に近い。
リフレクションなのでパフォーマンスはアレだが、1. のような副作用はない。

3. 不動点コンビネータで再帰

再帰メソッドを引数で渡せばいいかも?という発想から下記を定義する。

Func<Func<int, int>, Func<int, int>> fac3base = (Func<int, int> f) => {
    return (int x) => {
        return x > 1 ? x * f(x - 1) : 1;
    };
};

// 型推論バージョン
// Func<Func<int, int>, Func<int, int>> fac3base = f => x => x > 1 ? x * f(x-1) : 1;

ここで、fac3base の引数は?ということに頭を悩ませると思う。
なぜなら、渡したい中身は fac3base に定義されているから・・・

とりあえず、Func<Func<int, int>, Func<int, int>> から Func<int, int> を取り出す処理、つまり、
Func<
  Func<Func<int, int>, Func<int, int>>,
  Func<int, int>
>
となるメソッド/ラムダが欲しい、ということで不動点コンビネータFixed-point combinator)の出番である。

  • Z コンビネータ
wikipedia どおりの実装。

// 自身を引数に持つデリゲート ※外部に定義する
delegate T SelfApplicable<T>(SelfApplicable<T> self);

Func<Func<Func<int, int>, Func<int, int>>, Func<int, int>> Z =
(Func<Func<int, int>, Func<int, int>> f) => {
    SelfApplicable<Func<int, int>> Z1 = (SelfApplicable<Func<int, int>> x) => {
        return f((int y) => {
            return x(x)(y);
        }
    };
    return Z1(Z1);
};

// 型推論バージョン
// Func<Func<Func<int, int>, Func<int, int>>, Func<int, int>> Z = f => {
//     SelfApplicable<Func<int, int>> Z1 = x => f(y => x(x)(y));
//     return Z1(Z1);
// };

// 使用方法
var fac3z = Z(fac3base);
Console.WriteLine(fac3z(4)); // 24

  • Y コンビネータ
ここの引用。
※C# による実装なので、厳密には Z コンビネータ

// SelfApplicable は Z コンビネータと同じもの
SelfApplicable<Func<Func<Func<int, int>, Func<int, int>>, Func<int, int>>> Y =
(SelfApplicable<Func<Func<Func<int, int>, Func<int, int>>, Func<int, int>>> y) => {
    return (Func<Func<int, int>, Func<int, int>> f) => {
        return (int x) => {
            return f(y(y)(f))(x);
        };
    };
};
Func<Func<Func<int, int>, Func<int, int>>, Func<int, int>> YFix = Y(Y);

// 型推論バージョン
// SelfApplicable<Func<Func<Func<int, int>, Func<int, int>>, Func<int, int>>> Y =
// y => f => x => f(y(y)(f))(x);
// var YFix = Y(Y);

// 使用方法
var fac3y = YFix(fac3base);
Console.WriteLine(fac3y(4)); // 24

なんで動いてるの?という疑問については、wikipedia や引用元サイト参照。
不動点コンビネータとは、要するに fac3base に渡す引数を fac3base から作り出してくれるものである。

おまけ

3. の汎用メソッド

public static class Combinators<TIn, TOut>
{
    static Combinators()
    {
        SelfApplicable<Func<Func<Func<TIn, TOut>, Func<TIn, TOut>>, Func<TIn, TOut>>>
        Y = y => f => x => f(y(y)(f))(x);
        YCombinator = Y(Y);
    }

    public static readonly
    Func<Func<Func<TIn, TOut>, Func<TIn, TOut>>, Func<TIn, TOut>> ZCombinator = f => {
        SelfApplicable<Func<TIn, TOut>> Z1 = x => f(y => x(x)(y));
        return Z1(Z1);
    };

    public static readonly
    Func<Func<Func<TIn, TOut>, Func<TIn, TOut>>, Func<TIn, TOut>> YCombinator;
}
public delegate T SelfApplicable<T>(SelfApplicable<T> self);

参考URL