Rev. 2.73

최근 블로그에 Ajax를 반영하면서 맞닥드린 난관은 브라우저의 뒤로가기, 앞으로가기 그리고 새로고침 버튼입니다. Ajax로 불러들인 문서는 페이지를 로드하지 않고 필요한 부분만 가지고 오기 때문에 브라우저의 히스토리에 흔적을 남기지 않습니다. 덕분에 유저들이 가장 많이 사용하는 브라우저의 기본기능 중 하나인 뒤로가기 버튼이 제대로 작동하지 않아 혼란을 겪게되고, 방문객은 본의 아니게 사이트를 떠나게 될수도 있다고 경고하는 출판물을 접한적이 있습니다.

이 문제를 해결하고자 이곳 저곳을 싸돌아 다니던 중 The Rails-spinoffs Archives에 'Ajax Bookmarks & Browser Navigation'라는 주제로 작성된 Ajax사용시 백버튼 이슈 중에서 Siegfried Puchbauer씨가 작성한 내용과 코드를 발견했습니다. Prototype의 Ajax.request 또는 Ajax.update를 사용할 때 해시 히스토리를 만들어 앞으로/뒤로 가기 문제를 해결하고 있습니다. 이것은 구글이 즐겨 사용하는 Ajax트릭과 흡사합니다.(예를 들면 피카사 웹앨범)

// Author: Siegfried Puchbauer <rails-spinoffs@lists.rubyonrails.org>
Ajax.History = {
    initialize: function(options) {
        this.options = Object.extend({
            interval: 200
        },options||{});
        this.callback = this.options.callback || Prototype.emtpyfunction;
        if(navigator.userAgent.toLowerCase().indexOf('msie') > 0)
            this.locator = new Ajax.History.Iframe('ajaxHistoryHandler', this.options.iframeSrc);
        else
            this.locator = new Ajax.History.Hash();
        this.currentHash = '';
        this.locked = false;
    },
    add: function(hash) {
        this.locked = true;
        clearTimeout(this.timer);
        this.currentHash = hash;
        this.locator.setHash(hash);
        this.timer = setTimeout(this.checkHash.bind(this), this.options.interval);
        this.locked = false;
    },
    checkHash: function(){
        if(!this.locked){
            var check = this.locator.getHash();

            if(check != this.currentHash){
                this.callback(check);
                this.currentHash = check;
            }
        }
        this.timer = setTimeout(this.checkHash.bind(this), this.options.interval);
    },
    getBookmark: function(){
        return this.locator.getBookmark();
    }
};

// Hash Handler for IE (Tested with IE6)
Ajax.History.Iframe = Class.create();
Ajax.History.Iframe.prototype = {
    initialize: function(id, src) {
        this.url = '';
        this.id = id || 'ajaxHistoryHandler';
        this.src = src || '';
        document.write('<iframe src="'+this.src+'" id="'+this.id+'"name="'+this.id+'" style="display: none;" ></iframe>');
    },
    setHash: function(hash){
        try {
            $(this.id).setAttribute('src', this.src + '?' + hash);
        }catch(e) {}
        window.location.href = this.url + '#' + hash;
    },
    getHash: function(){
        try {
            return (document.frames[this.id].location.href||'?').split('?')[1];
        }catch(e){ return ''; }
    },
    getBookmark: function(){
        try{
            return window.location.href.split('#')[1]||'';
        }catch(e){ return ''; }
    }
};

// Hash Handler for a modern browser (tested with firefox 1.5)
Ajax.History.Hash = Class.create();
Ajax.History.Hash.prototype = {
    initialize: function(){
    },
    setHash: function(hash){
        window.location.hash = hash;
    },
    getHash: function(){
        return window.location.hash.substring(1)||'';
    },
    getBookmark: function(){
        try{
            return window.location.hash.substring(1)||'';
        }catch(e){ return ''; }
    }
};

위와 같은 코드를 로드한 후 아래처럼 콜백 메서드를 히스토리 핸들러에 Ajax 요청을 보낼 수 있습니다.

var historyUpdate = function(hash) {
	new Ajax.Request(
	....
}

Prototype의 Ajax.Request 또는 Ajax.Updater를 사용할 때 onSuccess 및 onComplete 메서드에 히스토리를 아래와 같이 추가할 수 있습니다.

var handleSuccess = function(request){
    //... update DOM or smth like that
    Ajax.History.add(request.options.parameters)
    ...
}

그리고 페이지 푸터에 아래의 코드를 실행하도록 합니다.

var historyHandler = Ajax.History.initialize({
    callback: historyUpdate,
    iframeSrc: '_blank.html' // 비어있는 임시 html 페이지 경로
});

해시가 추가된 주소로 접근했을 때의 액션은 정의되지 않았지만, Scriptaculous 프레임웍 기반 구찌(GUCCI)는 해시를 가진 상품주소로 접근한 경우 해당 상품정보로 멋지게 연결하고 있습니다. 참고해 봅시다.

2008-10-2 여러분의 요청으로 간단한 예제파일을 만들어 첨부합니다.

Ajax.History.Example.zip Ajax.History.Example.zip (2.1 KB)

Comments