IllustPopupManager beta2

現在開発中の JavaScript モジュール "IllustPopupManager" についてのページです。

"IllustPopupManager" について

主として写真や絵などの画像を扱うページでは、サムネイルが一覧として表示され、サムネイルをクリックすると通常サイズの画像を置いたページ (または画像そのもの) に遷移するという造りになっていることが多いと思います。 ただ、ページの遷移は閲覧者からしても製作者からしてもやや面倒くさいのではないでしょうか。

"IllustPopupManager" を使うと、HTML 側では画像へのリンクを貼っておき、そのリンクがクリックされると JavaScript によってイベントを検出、リンク先の画像を (ページ遷移することなく) 画面の中央にポップアップ表示する、ということができます。 "LigthBox" と同じようなものです。 (機能的には数段劣ってますが笑)

sample

実際に動作するサンプルです。 下の画像をクリックすると、JavaScript および CSS が有効な環境では画面中央に大きな絵が表示されるはずです。

Firefox 3、Opera 9.5、Safari 3、Chrome beta、Internet Explorer 6, 7, 8beta2 での動作確認をしております。

使い方

JS モジュール "IllustPopupManager" を HTML の header 部で読み込み、

<a href="xxx.jpg" class="popup">...</a>

という風に、ページ内の画像へのリンクを表す要素 "a" に、"popup" という class を付けるだけです。 あとは JavaScript 側で自動的に処理します。

JS モジュール "IllustPopupManager" は次節の source code を適当な名前 ("ipm.js" とか) で保存したらそのまま使えるはずです。

source code

JS モジュール "IllustPopupManager" の source code です。 まだ beta 版なんで変な箇所があるかもしれません。 改善案など送ってもらえると助かります。
連絡先: webmaster(あっと)r-definition.com

設定項目は 1 つだけあります。 画像ポップアップ表示時に、背景に表示される画像の URL を設定してください。 変更しなくても使用できますが、うちのサーバにある画像を参照することになるためオススメしません。 変更することを強く推奨します。

ipm_beta2.js の source code
/**
 * IllustPopupManager
 *   画像へのリンクを、新しいページに遷移せず、現在のページ内で開くための
 *   JavaScript モジュールです. 
 *   大域的名前空間では "IllustPopupManager" という変数しか使いません. 
 */
var IllustPopupManager = function() {
    this.version = "beta2";
};

//------------------------------
// 設定項目
//------------------------------

/** 背景画像の設定: 絶対 URL で指定すること */
IllustPopupManager.bgImage = "http://www.r-definition.com/program/js/ud/ipm/bg.png";


//------------------------------
// 処理内容
//------------------------------
IllustPopupManager.targetTable = new Array(0);

// a function which adds the event listener
IllustPopupManager.addListener = (function() {
    if ( window.addEventListener ) { // W3C 準拠ブラウザ用
        return function(elem, type, func) {
            elem.addEventListener(type, func, false);
        };
    } else if ( window.attachEvent ) { // IE 用
        return function(elem, type, func) {
            if( ! func._bridge ) {
                func._bridge = new Array(0);
            }
            var i = func._bridge.length;
            func._bridge[i] = new Array(3);
            func._bridge[i][0] = elem;
            func._bridge[i][1] = type;
            func._bridge[i][2] = function() {
                func.apply(elem, arguments);
            };
            elem.attachEvent("on"+type, func._bridge[i][2]);
        };
    } else {
        return function(elem, type, func) {
            return false;
        }
    }
})();

// a function which removes the event listener
IllustPopupManager.removeListener = (function() {
    if ( window.removeEventListener ) {
        return function(elem, type, func) {
            elem.removeEventListener(type, func, false);
        };
    } else if ( window.detachEvent ) {
        return function(elem, type, func) {
            var i = 0; var f = null;
            while( i < func._bridge.length ) {
                if( func._bridge[i][0] == elem && func._bridge[i][1] == type ) {
                    f = func._bridge[i][2];
                    break;
                }
                i++;
            }
            elem.detachEvent("on"+type, f);
            func._bridge.splice(i,1);
        };
    } else {
        return function(elem, type, func) {
            return false;
        }
    }
})();

// class が "illust" である node を取得する
IllustPopupManager.searchByClass = function(aNode, aClassName) {
    //alert(node);
    var resNodes = new Array(0);
    if( aNode.nodeType != 1 ) {
        return resNodes;
    }
    // var className = aNode.getAttribute("class"); // IE6,7 で不可
    var classNames = aNode.className.split(" "); // 複数の class 名が記述してある場合用
    for( var i = 0; i < classNames.length; i++ ) {
        if( classNames[i] == aClassName ) {
            resNodes.push(aNode);
            break;
        }
    }
    var children = aNode.childNodes; // NodeList
    for( var i = 0; i < children.length; i++ ) {
        var retNodes = IllustPopupManager.searchByClass( children.item(i), aClassName );
        resNodes = resNodes.concat(retNodes);
    }
    return resNodes;
}

/**
 * 画像を新しく画面の中央に配置する.
 * @param aNode event が発生した node
 * @param aEL この関数を呼び出す eventListner. 関数呼出し後に eventListener は取り除く
 */
IllustPopupManager.showIllust = function( aNode ) {
    //IllustPopupManager.removeListener( aNode, "click", aEL );
    
    var width  = null;
    var height = null;
    var style     = null;
    var boxStyle  = null;
    var src = aNode.getAttribute("href");
    if( src.indexOf("?") >= 0 ) {
        var tmp = src.split("?");
        src = tmp[0];
        tmp = tmp[1].split("&");
        for( var i = 0; i < tmp.length; i++ ) {
            if( tmp[i].match(/^width=\d+/) != null ) {
                width = "width: " + tmp[i].substr(6) + "px;";
            }
            if( tmp[i].match(/^height=\d+/) != null ) {
                var h = tmp[i].substr(7);
                h /= 2;
                height = "height: " + tmp[i].substr(7) + "px;";
            }
        }
        style = width + height;
    }
    
    var isIE6 = true;
    if( typeof document.body.style.maxHeight != "undefined" ) {
        isIE6 = false;
    }
    
    if( isIE6 ) { // IE 6
        var node = document.documentElement;
        node = node.getElementsByTagName("body");
        node = node[0];
    } else { // mozilla, safari, opera, ie7, ...
        var node = aNode.parentNode;
    }
    
    var box   = document.createElement("div");
    //box.className = "popupIllust";
    if( isIE6 ) {
        box.style.cssText = "position: absolute;" + 
            "filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + IllustPopupManager.bgImage + "',sizingMethod='scale');" + 
            "width: expression(document.documentElement.clientWidth);" + 
            "height: expression(document.documentElement.clientHeight);" + 
            "top: expression(document.documentElement && document.documentElement.scrollTop || document.body && body.scrollTop || 0 + 0 + 'px' );" + 
            "left:expression( document.documentElement && document.documentElement.scrollLeft || document.body && body.scrollLeft || 0 + 0 + 'px' );";
    } else {
        box.style.cssText = "position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; " + 
            "background-image: url(\"" + IllustPopupManager.bgImage + "\");" + 
            "text-align: center; line-height: 100%;";
    }
    var t1 = document.createElement("table");
    t1.style.cssText = "width: 100%; height: 100%;";
    if( boxStyle != null ) {
        box2.style.cssText = boxStyle;
    }
    var t2 = document.createElement("tbody");
    var t3 = document.createElement("tr");
    var box2 = document.createElement("td");
    var box3 = document.createElement("div");
    box3.style.cssText = "border: solid 3px #999999; margin: auto; " + width + height;
    var image = document.createElement("img");
    image.setAttribute("src", aNode.getAttribute("href"));
    image.setAttribute("alt", "");
    if( style != null ) {
        image.style.cssText = style;
    }
    box.appendChild(t1);
    t1.appendChild(t2);
    t2.appendChild(t3);
    t3.appendChild(box2);
    box2.appendChild(box3);
    box3.appendChild(image);
    node.appendChild(box);
    // 登録
    box.popupRelation = aNode;
    var i = 0;
    while( i < IllustPopupManager.targetTable.length ){
        if( IllustPopupManager.targetTable[i][0] == aNode ) {
            IllustPopupManager.targetTable[i][1] = box;
            break;
        }
        i++;
    }
    //IllustPopupManager.targetTable[i][1] = box;
    IllustPopupManager.removeListener( aNode, "click", IllustPopupManager.showEventListener );
    IllustPopupManager.addListener( box, "click", IllustPopupManager.delEventListener );
    IllustPopupManager.addListener( aNode, "click", IllustPopupManager.delEventListener );
}
IllustPopupManager.showEventListener = function(e) {
    //alert( e.type + " " + this.getAttribute("href") );
    // ページ遷移 (本来のイベント) の停止
    IllustPopupManager.showIllust(this);
    if(e.preventDefault) { e.preventDefault(); }
    else { event.returnValue = false; } // IE 用
};

IllustPopupManager.delIllust = function(aNode) {
    var ancNode;
    var boxNode;
    var parent;
    if( aNode.popupRelation != undefined ) {
        ancNode = aNode.popupRelation;
        boxNode = aNode;
    } else {
        ancNode = aNode;
        var i = 0;
        while( i < IllustPopupManager.targetTable.length ){
            if( IllustPopupManager.targetTable[i][0] == aNode ) {
                boxNode = IllustPopupManager.targetTable[i][1];
                break;
            }
            i++;
        }
        IllustPopupManager.targetTable[i][1] = null;
    }
    parent = boxNode.parentNode;
    // opera だとそのまま削除すると部分的にしか再描画されない
    // よって、まずは "display: none" に設定して、1ms 後に DOM 木から取り除く
    boxNode.style.display = 'none';
    setTimeout( function() {parent.removeChild(boxNode);}, 1);
    // set the popup event to the element "a"
    /*
    var eventListener = function(e) {
        //alert( e.type + " " + this.getAttribute("href") );
        // stop the default event (=moving the page)
        alert("event show");
        IllustPopupManager.showIllust(aNode, eventListener);
        if(e.preventDefault) { e.preventDefault(); }
        else { event.returnValue = false; } // IE 用
    };
    */
    IllustPopupManager.removeListener( ancNode, "click", IllustPopupManager.delEventListener );
    //IllustPopupManager.removeListener( aBox, "click", aEL );
    IllustPopupManager.addListener( ancNode, "click", IllustPopupManager.showEventListener );
};
IllustPopupManager.delEventListener = function(e) {
    IllustPopupManager.delIllust(this);
    if(e.preventDefault) { e.preventDefault(); }
    else { event.returnValue = false; } // IE 用
};

// a function which is executed when the page have been loaded
IllustPopupManager.init = function(e) {
    
    var root = document.documentElement; // get the root element
    var list = IllustPopupManager.searchByClass(root, "popup"); // search target nodes
    
    for( var i = 0; i < list.length; i++ ) {
        //var nodes = list[i].getElementsByTagName("a");
        //var node = nodes[0];
        var node = list[i];
        IllustPopupManager.targetTable[i] = new Array(2);
        IllustPopupManager.targetTable[i][0] = node;
        IllustPopupManager.targetTable[i][1] = null;
        IllustPopupManager.addListener( node, "click", IllustPopupManager.showEventListener );
    }
    
};

// window オブジェクトに EventListener として init 関数を追加
IllustPopupManager.addListener(window, "load", IllustPopupManager.init);

現在の課題と今後の方針

忘れないように自分用にメモ。

現在の問題点は

という感じですか。

方針としてはそこら辺の課題を解決しつつ code をよりよくしていく、って感じで。 できれば object 指向で書きたいんだけど JavaScript は class ベースじゃないからわけわかんないんですよねー。