2013-01-08 3 views
19

콘텐츠로드 ajax로 HTML5 기록 API를 시험하고 있습니다.Ajax with history.pushState 및 popstate - popstate state 속성이 null 일 때 나는 무엇을해야합니까?

나는 상대 링크로 연결된 테스트 페이지를 가지고있다. 나는이 링크를 클릭하는 JS를 가지고있다. 링크가 클릭되면 핸들러는 href 속성을 잡고 요청 된 페이지의 컨텐츠를 현재 페이지의 컨텐츠 영역으로로드하는 ajaxLoadPage()에 전달합니다. (PHP 페이지는 정상적으로 요청할 경우 전체 HTML 페이지를 반환하지만 요청의 URL에? fragment = true가 추가되면 콘텐츠 청크 만 반환됩니다.)

내 클릭 핸들러가 history를 호출합니다. .pushState()를 사용하여 주소 표시 줄에 URL을 표시하고 브라우저 기록에 추가하십시오.

$(document).ready(function(){ 

    var content = $('#content'); 

    var ajaxLoadPage = function (url) { 
     console.log('Loading ' + url + ' fragment'); 
     content.load(url + '?fragment=true'); 
    } 

    // Handle click event of all links with href not starting with http, https or # 
    $('a').not('[href^=http], [href^=https], [href^=#]').on('click', function(e){ 

     e.preventDefault(); 
     var href = $(this).attr('href'); 
     ajaxLoadPage(href); 
     history.pushState({page:href}, null, href); 

    }); 

    // This mostly works - only problem is when popstate happens and state is null 
    // e.g. when we try to go back to the initial page we loaded normally 
    $(window).bind('popstate', function(event){ 
     console.log('Popstate'); 
     var state = event.originalEvent.state; 
     console.log(state); 
     if (state !== null) { 
      if (state.page !== undefined) { 
       ajaxLoadPage(state.page); 
      } 
     } 
    }); 

}); 

당신은 당신은 또한 뒤로 또는 앞으로 버튼 클릭을 처리 할 수있는 popstate 이벤트에 대한 이벤트 핸들러를 포함 할 필요가 pushState와 역사에 URL을 추가합니다. (이 작업을 수행하지 않으면 뒤로를 클릭하면 내 주소록에 기록에 푸시 된 URL이 표시되지만 페이지는 업데이트되지 않습니다.) 그래서 내 popstate 처리기는 내가 만든 각 항목의 state 속성에 저장된 URL을 가져옵니다. 적절한 내용을로드하기 위해 ajaxLoadPage에 전달합니다.

내 클릭 핸들러가 기록에 추가 된 페이지에는 정상입니다. 하지만 브라우저에서 "정상적으로"요청했을 때 브라우저에서 기록에 추가 한 페이지는 어떻게됩니까? 정상적으로 첫 페이지에 착륙 한 다음 Ajax 로딩을 수행하는 클릭으로 내 사이트를 탐색한다고 가정 해 봅니다. 그런 다음 첫 페이지에 대한 기록을 검토하려고하면 마지막 클릭이 첫 번째 페이지의 URL을 표시하지만 doesn 브라우저에 페이지를로드하지 마십시오. 왜 그런가요?

나는 이것이 마지막 popstate 이벤트의 state 속성과 관련이 있다는 것을 알 수 있습니다. state에 대한 속성은 pushState() 또는 replaceState()에 의해 히스토리에 추가 된 항목 일 뿐이므로 그 이벤트에 대해서는 null입니다. 그러나 페이지를 처음로드하는 것은 "정상적인"요청이었습니다. 브라우저가 원래 단계로 돌아가서 정상적인 URL을로드하지 않는 이유는 무엇입니까?

답변

4

왜 나는 뒤로 버튼이 이와 같이 작동하는지 이해할 수 없습니다. 브라우저가 정상적인 요청으로 생성 된 항목으로 되돌아 가서 기꺼이 돌아갈 것이라고 생각했을 것입니다. 어쩌면 pushState를 사용하여 다른 항목을 삽입하면 기록이 정상적인 방식으로 작동하지 않게됩니다. 하지만 코드를 더 잘 작동시키는 방법을 찾았습니다. 다시 되돌리려는 URL이 포함 된 상태 속성에 항상 의존 할 수는 없습니다. 그러나 기록을 단계별로 살펴보면 예상대로 주소 표시 줄의 URL이 변경되므로 window.location을 기반으로 콘텐츠를로드하는 것이 더 안정적 일 수 있습니다. 이 great example 다음 상태 속성에서 URL을 찾는 대신 주소 표시 줄에있는 URL을 기반으로 내용을로드하도록 popstate 처리기를 변경했습니다.

주의해야 할 점 중 하나는 Chrome과 같은 일부 브라우저가 처음 페이지를 클릭했을 때 팝업 이벤트를 발생시키는 것입니다. 이런 일이 발생하면 불필요하게 초기 페이지의 콘텐츠를 다시로드해야합니다. 그래서 나는 그 우수 팝업을 무시하기 위해 우수 pjax의 일부 코드를 추가했습니다.당신이 JS로 만들 수

$(document).ready(function(){ 

    // Used to detect initial (useless) popstate. 
    // If history.state exists, pushState() has created the current entry so we can 
    // assume browser isn't going to fire initial popstate 
    var popped = ('state' in window.history && window.history.state !== null), initialURL = location.href; 

    var content = $('#content'); 

    var ajaxLoadPage = function (url) { 

     console.log('Loading ' + url + ' fragment'); 
     content.load(url + '?fragment=true'); 

    } 

    // Handle click event of all links with href not starting with http, https or # 
    $('a').not('[href^=http], [href^=https], [href^=#]').on('click', function(e){ 

     e.preventDefault(); 
     var href = $(this).attr('href'); 
     ajaxLoadPage(href); 
     history.pushState({page:href}, null, href); 

    }); 

    $(window).bind('popstate', function(event){ 

     // Ignore inital popstate that some browsers fire on page load 
     var initialPop = !popped && location.href == initialURL; 
     popped = true; 
     if (initialPop) return; 

     console.log('Popstate'); 

     // By the time popstate has fired, location.pathname has been changed 
     ajaxLoadPage(location.pathname); 

    }); 

}); 

한 개선은 브라우저가 역사의 API를 지원하는 경우 클릭 이벤트 처리기를 연결하는 전용입니다.

+0

하나의 작은 문제로 화격자가 작동합니다. 페이지를 다시로드하면 페이지의 아약스 부분 만로드됩니다. 누구나 페이지를 다시로드 할 수있는 솔루션이 있는지 알고 싶습니다. – Hydrino

+0

@Hydrino 올바른 URL로 상태를 누르고 AJAX로 파트 페이지를로드하십시오. –

1

나는 실제로 비슷한 필요성을 안고 있었고 제공 한 코드가 매우 유용하다고 생각했습니다. 나는 당신이했던 것과 똑같은 문제에 봉착했습니다. 당신이 잃어버린 것은 모두 당신이 모든 후속 페이지와 동일한 방식으로 당신의 인덱스 파일이나 홈페이지를 히스토리로 밀어 넣는 것이라고 믿습니다.

여기에 나는이 해결하기 위해 무슨 짓을했는지의 예입니다 (그것이 정답입니다 있는지 확실하지 않습니다, 그러나 그것은 간단하고 그것을 작동합니다!) :이 도움이

var home = 'index.html'; 
history.pushState({page:home}, null, home); 

희망!

+0

감사합니다. mattblac, 더 유연한 방법을 찾았습니다. 여기에 게시하겠습니다. –

6

원래 질문과 동일한 문제가 발생했습니다. 이 줄

var initialPop = !popped && location.href == initialURL; 

이 초기 팝업을 잡기에 충분하다

var initialPop = !popped; 

로 변경해야합니다. 그런 다음 원본 페이지를 pushState에 추가 할 필요가 없습니다.

if (this.supports_history_api()) { 
    var popped = ('state' in window.history && window.history.state !== null) 
    , changeTabBack = false; 

    window.addEvent('myShowTabEvent', function (url) { 
     if (url && !changingTabBack) 
      setLocation(url); 
     else 
      changingTabBack = false; 
     //Make sure you do not add to the pushState after clicking the back button 
    }); 

    window.addEventListener("popstate", function(e) { 
     var initialPop = !popped; 
     popped = true; 
     if (initialPop) 
      return; 

     var tabLink = $$('a[href="' + location.pathname + '"][data-toggle*=tab]')[0]; 
     if (tabLink) { 
      changingTabBack = true; 
      tabLink.tab('show'); 
     } 
    }); 
} 
+0

좋아요! 좋은 답변 에린. –

12

이것은 오래된 질문이지만 훨씬 간단한 대답은이 문제에 대한 기본 자바 스크립트를 사용하여이 : AJAX 탭을 기반으로

var home = 'index.html'; 
history.pushState({page:home}, null, home); 

마지막 코드 (및 사용 Mootools의) : 즉, 다음을 제거 .

초기 상태는 history.pushState이 아니라 history.replaceState을 사용해야합니다.

모든 인수는 두 가지 방법이 동일하지만 pushState은 새로운 기록 레코드를 작성하므로 문제의 원인입니다. replaceState은 해당 내역 레코드의 상태를 바꿀 뿐이며 예상대로 작동합니다. 즉 초기 시작 페이지로 돌아갑니다.

+1

이것은 내 문제에 대한 해답이었습니다. 처음 페이지에 올 때 ajax를 사용하여로드하면이 것을 사용하여 첫 번째 상태를 바꿔야합니다. 감사! – user1689571

0

나는이 오래된 질문 실현하지만, 쉽게 이런 상태를 관리 할 때, 다음과 같은 방법이 걸릴 더 좋을 수 있습니다

$(window).on('popstate',function(e){ 
    var state = e.originalEvent.state; 
    if(state != null){ 
     if(state.hasOwnProperty('window')){ 
      //callback on window 
      window[state.window].call(window,state); 
     } 
    } 
}); 

이 방법을 선택 사양 인 콜백 함수를 지정할 수 있습니다 히스토리에 추가 할 때 상태 객체에서 popstate이 트리거되면 상태 객체를 매개 변수로 사용하여이 함수를 호출합니다.

function pushState(title,url,callback) 
{ 
    var state = { 
     Url : url, 
     Title : title, 
    }; 
    if(window[callback] && typeof window[callback] === 'function') 
    { 
     state.callback = callback; 
    } 
    history.pushState(state,state.Title,state.Url); 
} 

필요에 맞게 쉽게 확장 할 수 있습니다.

-1

그리고 마지막으로는 말한다 :

나는 브라우저가 일반 요청에 의해 생성 된 엔트리에 한 걸음 뒤로 물러나 행복 할 것이라고 생각했을 것

.

이상한 브라우저의 동작에 대한 설명을 찾았습니다. here. 설명은 귀하의 사이트가이 상태를 나는이 테스트

을 변경 때마다 처음을로드하고 그 후에 때 당신이 상태를 저장해야

- 그것은 작동합니다.

즉, 은 window.location을 기반으로 컨텐츠를로드 할 필요가 없음을 의미합니다.

나는 오해하지 않기를 바랍니다.

관련 문제