หน้าเว็บ

วันศุกร์ที่ 22 มีนาคม พ.ศ. 2556

detect url hash position change javascript


คำถาม  เอาไว้ใช้ทำอะไร?
ตอบ
        ถ้าใครเขียน web แล้วทำ push state ajax โดยใช้วิธี  hash change (เช่น twitter, facebook, ...)   ก็จะได้ใช้ครับ (คือผมเอา hash (location.hash) มาระบุ module ต่างๆ ที่จะนำมาแสดงผล)  แต่ปัญหาที่เจอคือ default event  javascript มัน detect ได้แค่ hash รวม (hash ทั้งหมด)  เท่านั้น

        ในกรณีที่ hash เราเป็นแบบนี้  #/unseen/post/tab1 => #/unseen/image/tab2  แล้วเราต้องการ detect ว่า  path (index) ไหนมีการเปลี่ยนแปลงไป  หรือเขียน javascript event ผูกติดกับ path นั้นๆ  เราจะทำยังไง  ซึ่งที่ผมยกตัวอย่างให้ดู คือจาก  post เปลี่ยนไปเปลี่ยน image  ซึ่งก็คือ path 1 (unseen เป็น path index 0) ผมก็จะเอา ajax ไปโหลด module /unseen/image มาแสดงผลครับ  มันจะไม่เอา tab2 มาด้วย  เนื่องจาก tab2 มันเป็น sub module ของ image  คือถ้า image มา tab2 ก็จะโหลดติดมาด้วยเองครับ (ที่ผม design ไว้)

        #/unseen/image/tab1 => #/unseen/statistic/tab1  ผมก็จะเอา ajax ไปโหลด module /unseen/statistic มาแสดงผล  เนื่องจาก hash[1] หรือ path 1 มีการเปลี่ยนแปลง

        #/profile/image/old_image => #/profile/image/new_image ผมก็จะเอา ajax ไปโหลด module /profile/image/new_image มาแสดงผล  เนื่องจาก hash[2] หรือ path 2 มีการเปลี่ยนแปลง  ความหมายก็ประมาณนี้ครับ

        ทีนี้ก็จะมีคนตั้งคำถามขึ้นมาอีกว่า  ทำไมต้องเฉพาะ path ?  เต็ม path ก็ได้นี่

        เนื่องจากผมเขียนหน้าเว็บให้เป็น module ย่อยครับ  ผมเลยต้องการ detect เฉพาะ module ย่อย ที่มีการเปลี่ยนแปลงเท่านั้น  เพื่อที่จะได้ใช้ ajax ไป load เอาเฉพาะ module ที่เราต้องการเอามาแสดงผล  ซึ่งมันก็จะช่วยเพิ่มความเร็วในการทำงานของ website เรา  เพราะเรา load เฉพาะที่จำเป็นต้องใช้เท่านั้นครับ

        ผมต้องการเขียนเว็บที่ปราศจากการ refresh หน้าเว็บ  แต่ browser ยังสามารถจดจำ page นั้นๆ  ไว้ได้  ทำไมต้องเขียนแบบนี้ล่ะ  เพราะผมอยากทำ web ที่มีมาตรฐานเทียบเท่ากับ website ระดับโลกเขายังไงล่ะครับ  ^___^

/**
 * @author redcrow
 * @link http://na5cent.blogspot.com
 * @create 23/03/2013
 * @update 27/03/2013
 */

var na5cent = {
    FIRST_PAGE : '/home',
    PREFIX_HASH : '#!'
};





na5cent.Urls = (function(na5cent) {
    return {
        getHash : function(){
            return location.hash.substr(na5cent.PREFIX_HASH.length);
        }
    };
})(na5cent);





na5cent.Event = {};

na5cent.Event.onHashChange = (function(window, na5cent) {
    var _SLASH = '/';





    var _oldPathArray = _getPathArray();
    var _callbackList = [];





    window.onhashchange = function() {
        var newPathArray = _getPathArray();

        var pathLength = ((newPathArray.length > _oldPathArray.length) ? newPathArray.length : _oldPathArray.length);

        for (var pathIndex = 0; pathIndex < pathLength; pathIndex++) {

            if (_oldPathArray[pathIndex] !== newPathArray[pathIndex]) {
                _deleteCallbackScope(pathIndex + 1);
                console.log('paths[' + pathIndex + '] changed[ from "' + _oldPathArray[pathIndex] + '" to "' + newPathArray[pathIndex] + '"]');

                _callbackList[pathIndex] = _callbackList[pathIndex] || [];
                for (var callback in _callbackList[pathIndex]) {
                    if (_callbackList[pathIndex][callback] === undefined) {
                        _callbackList[pathIndex][callback] = {
                            success: function() {

                            },
                            defaultModule: '',
                            scope: pathIndex
                        };
                    }

                    var fullPath = '';
                    for (var i = 0; i <= pathIndex; i++) {
                        fullPath = fullPath + '/' + ((newPathArray[i] !== undefined) ? newPathArray[i] : _callbackList[pathIndex][callback].defaultModule);
                    }

                    _callbackList[pathIndex][callback].success(fullPath);
                }
                break;
            }
        }

        _oldPathArray = newPathArray;
    };





    function _deleteCallbackScope(pathIndex) {
        if (_callbackList[pathIndex] === undefined) {
            return;
        }

        for (var index in _callbackList) {
            if (index >= pathIndex) {
                for (var callback in _callbackList[pathIndex]) {
                    if (_callbackList[pathIndex][callback].scope >= (pathIndex)) {
                        delete _callbackList[pathIndex][callback];
                    }
                }
            }
        }
    }





    function _getPathArray() {
        var paths = [];

        var split = na5cent.Urls.getHash().split('/');
        for (var index in split) {
            if (split[index] !== '') {
                paths.push(split[index]);
            }
        }

        if (paths.length === 0) {
            var path = na5cent.FIRST_PAGE;
            if (na5cent.FIRST_PAGE[0] === _SLASH) {
                path = na5cent.FIRST_PAGE.substr(_SLASH.length);
            }

            paths = [path];
        }

        return paths;
    }





    function _ensureRequire(bindIndex, callback, defaultModule) {

        if (arguments.length < 3) {
            throw new Error('na5cent.onHashChange({number} bindIndex, {function} callback, {string} defultModule, {number} scope) require 3 arguments[index, callback, defultModule]');
        }

        var message = 'of na5cent.Event.onHashChange({number} bindIndex, {function} callback, {string} defultModule) is not';
        if (typeof bindIndex !== 'number') {
            throw new Error('First argument[bindIndex] ' + message + ' "number", but is "' + (typeof bindIndex) + '".');
        }

        if (typeof callback !== 'function') {
            throw new Error('Second argument[callback] ' + message + ' "function", but is "' + (typeof callback) + '".');
        }

        if (typeof defaultModule !== 'string') {
            throw new Error('Third argument[defaultModule] ' + message + ' "string", but is "' + (typeof defaultModule) + '".');
        }

        if (defaultModule[0] === _SLASH) {
            defaultModule = defaultModule.substr(_SLASH.length);
        }
    }





    return function(bindIndex, callback, defaultModule, scope) {
        _ensureRequire(bindIndex, callback, defaultModule);

        _callbackList[bindIndex] = _callbackList[bindIndex] || [];
        _callbackList[bindIndex].push({
            success: callback,
            defaultModule: defaultModule,
            scope: (scope !== undefined) ? scope : bindIndex //defualt index
        });
    };

})(window, na5cent);





/**
 * example to use
 */

//สามารถ รองรับ callback ได้หลาย function

//parameter ตัวแรก คือ path index ว่าเราจะ detect path index ไหน

//parameter ตัวที่ 2 คือ callback function  เมื่อ path index นั้นมีการเปลี่ยนแปลง มันจะทำงาน  และโยน path นั้นๆ  ออกมา

//parameter ตัวที่ 3 คือ default module ไว้ใช้ในกรณีที่ path ที่เปลี่ยนแปลงไปกลายเป็น undefined path

//parameter ตัวที่ 4 คือ scope ของการ delete callback function ออกจาก callbackList  เนื่องจากผมเขียนเว็บที่ไม่ได้มีการ refresh หน้าเว็บ  มันเลยจะต้องทำการ delete callback ใน callback list อยู่เป็นระยะๆ  ซึ่งผมให้มัน delete ตาม scope ของ index path ครับ  แต่ถ้าเราไม่อยากให้มันลบตาม index path นั้นๆ  เราก็สามารถกำหนดลงไปได้  ว่าจะให้มันลบเมื่อ path ไหนมีการเปลี่ยนแปลงแทน


na5cent.Event.onHashChange(0, function(path) {
    console.log('0:1 => ' + path);
}, 'unseen');

na5cent.Event.onHashChange(0, function(path) {
    console.log('0:2 => ' + path);
}, 'unseen');

na5cent.Event.onHashChange(0, function(path) {
    console.log('0:3 => ' + path);
}, 'unseen');



na5cent.Event.onHashChange(1, function(path) {
    console.log('1:1 => ' + path);
}, 'post');// callback นี้จะถูกลบออกจาก callbackList เมื่อ path 0 หรือ path 1 มีการเปลี่ยนแปลง

na5cent.Event.onHashChange(1, function(path) {
    console.log('1:2 => ' + path);
}, 'post', 0);// callback นี้จะถูกลบออกจาก callbackList เมื่อ path 0 มีการเปลี่ยนแปลงเท่านั้น

na5cent.Event.onHashChange(1, function(path) {
    console.log('1:3 => ' + path);
}, 'post', 0);



na5cent.Event.onHashChange(2, function(path) {
    console.log('2:1 => ' + path);
}, 'ttt', 0);// callback นี้จะถูกลบออกจาก callbackList เมื่อ path 0 มีการเปลี่ยนแปลงเท่านั้น

na5cent.Event.onHashChange(2, function(path) {
    console.log('2:2 => ' + path);
}, 'xxx');// callback นี้จะถูกลบออกจาก callbackList เมื่อ path 0 หรือ path 1 หรือ path 2 มีการเปลี่ยนแปลง

na5cent.Event.onHashChange(2, function(path) {
    console.log('2:3 => ' + path);
}, 'xxx');


//

ไม่มีความคิดเห็น:

แสดงความคิดเห็น