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

Got something to add? You can just leave a comment.

  • 안그래도 AJAX 를 접목시킨 통계 플러그인에서 "뒤로가기" 문제로 고민중이었는데 정말 반가운 정보를 만났습니다 :-)

    reply edit

  • 왕새우 왕새우

    테스트하다 발견한건데 오타가 있는듯하네요
    checkHash: function(){
    if(!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);
    },
    여기에서 locked는 this.locked 인거 같은데요

    reply edit

  • 옷! 죄송합니다. 입력과정에서 빼먹었나 봐요. 즉시 수정 반영하였습니다. 감사합니다.

    reply edit

  • 잇힝 잇힝

    저같은 초짜에겐 설명이 너무 부족합니다...
    따라하질 못하겠네요.. ㅠ_ㅠ

    reply edit

  • 죄송합니다. 설명이 많이 부족합니다. 이 코드의 작동원리는 0.02초마다 url의 변동상황을 검사하여 변화가 있는경우 Ajax 객체를 다시 호출하는 원리입니다. 아래의 js소스에서 위 히스토리 클래스를 일부분 사용하고 있습니다.(키워드: History)참고해 보시기 바랍니다.

    http://firejune.com/javascripts/extensions.js
    http://firejune.com/javascripts/firejune.js

    reply edit

  • jino jino

    뒤로가기때문에 고민중이었는데.. 좋은정보감사합니다. 제블로그로 담아갑니다
    .(출처는 게시하겠습니다. ^^)

    reply edit

  • 반땅 반땅

    샘플이 사파리와 익스플러워 7버젼에서는 잘 돌아가지 않는군요. ㅠㅠ
    크로스 브라우저용으로 lib스크립트를 만들수 있을까요??

    reply edit

Your Reaction Time!

captcha

avatar