페이지 스크롤없이 location.hash 수정


148

콘텐츠에로드하기 위해 ajax를 사용하는 몇 페이지가 있으며 페이지에 딥 링크해야하는 경우가 있습니다. "사용자"에 대한 링크를 갖고 사람들에게 "설정"을 클릭하도록 지시하는 대신 사람들을 user 에 연결할 수있는 것이 도움이됩니다.

사람들이 섹션 (기술 지원 등)에 대한 올바른 링크를 제공 할 수 있도록 버튼을 클릭 할 때마다 URL의 해시를 자동으로 수정하도록 설정했습니다. 물론 유일한 문제는 이런 일이 발생하면 페이지를이 요소로 스크롤한다는 것입니다.

이것을 비활성화하는 방법이 있습니까? 아래는 내가 지금까지 어떻게하고 있는지입니다.

$(function(){
    //This emulates a click on the correct button on page load
    if(document.location.hash){
     $("#buttons li a").removeClass('selected');
     s=$(document.location.hash).addClass('selected').attr("href").replace("javascript:","");
     eval(s);
    }

    //Click a button to change the hash
    $("#buttons li a").click(function(){
            $("#buttons li a").removeClass('selected');
            $(this).addClass('selected');
            document.location.hash=$(this).attr("id")
            //return false;
    });
});

return false;페이지가 스크롤 되는 것을 막기를 원했지만 링크가 전혀 작동하지 않습니다. 이제는 주석 처리되었으므로 탐색 할 수 있습니다.

어떤 아이디어?

답변:


111

1 단계 : 해시가 설정 될 때까지 노드 ID를 제거해야합니다. 해시가 설정되는 동안 노드에서 ID를 제거한 다음 다시 추가하면됩니다.

hash = hash.replace( /^#/, '' );
var node = $( '#' + hash );
if ( node.length ) {
  node.attr( 'id', '' );
}
document.location.hash = hash;
if ( node.length ) {
  node.attr( 'id', hash );
}

2 단계 : 일부 브라우저는 ID의 노드가 마지막으로 표시된 위치를 기반으로 스크롤을 트리거하므로 약간의 도움이 필요합니다. div뷰포트 상단에 추가를 추가 하고 ID를 해시로 설정 한 다음 모든 것을 롤백해야합니다.

hash = hash.replace( /^#/, '' );
var fx, node = $( '#' + hash );
if ( node.length ) {
  node.attr( 'id', '' );
  fx = $( '<div></div>' )
          .css({
              position:'absolute',
              visibility:'hidden',
              top: $(document).scrollTop() + 'px'
          })
          .attr( 'id', hash )
          .appendTo( document.body );
}
document.location.hash = hash;
if ( node.length ) {
  fx.remove();
  node.attr( 'id', hash );
}

3 단계 : 플러그인으로 감싸서 쓰는 대신 사용하십시오 location.hash...


1
아니요, 2 단계에서 발생하는 일은 숨겨진 div가 생성되어 스크롤의 현재 위치에 배치된다는 것입니다. position : fixed / top : 0과 시각적으로 동일합니다. 따라서 스크롤바는 현재 위치와 동일한 지점으로 "이동"됩니다.
Borgar

3
이 솔루션은 실제로 잘 작동 - 그러나 선 : 상단 : $ .scroll () 상단 + '픽셀'을해야합니다 상단 : $ (창) .scrollTop () + 'PX'.
마크 퍼킨스

27
여기에서 2 단계가 필요한 브라우저를 아는 것이 유용합니다.
djc

1
감사합니다 Borgar. 대상 노드의 ID를 삭제하기 전에 fx div를 추가하고 있지만 당황합니다. 즉, 문서에 ID가 중복되는 순간이 있습니다. 잠재적 인 문제 또는 적어도 나쁜 방법처럼 보입니다.)
Ben

1
정말 감사합니다. 이것 또한 도움이되었습니다. 그러나 나는 이것이 작동 할 때 부작용을 발견했다. 나는 종종 "중간 마우스 버튼"을 잠그고 단순히 스크롤하려는 방향으로 마우스를 움직여 스크롤한다. Chrome에서는 스크롤 흐름이 중단됩니다. 그것에 대한 멋진 해결 방법이 있습니까?
YMMD

99

해시를 변경 하려면 history.replaceState또는 history.pushState*를 사용하십시오 . 연결된 요소로 점프하지 않습니다.

$(document).on('click', 'a[href^=#]', function(event) {
  event.preventDefault();
  history.pushState({}, '', this.href);
});

JSFiddle 데모

* 당신은 역사를 앞뒤로 지원하려는 경우

역사 행동

당신이 사용하는 경우 history.pushState당신은 사용자가 실험 체크 아웃 브라우저 (전방 / 후방)의 역사를 버튼을 사용하면 페이지를 스크롤하지 않으려는 scrollRestoration설정 (만 46+ 크롬) .

history.scrollRestoration = 'manual';

브라우저 지원


5
replaceState아마 여기가는 더 좋은 방법 일 것입니다. 차이는 pushState기록에 항목을 추가하지만 replaceState그렇지는 않습니다.
Aakil Fernandes

@AakilFernandes에게 감사합니다. 방금 답변을 업데이트했습니다.
HaNdTriX

항상 body스크롤 되는 것은 아니며 때로는 documentElement입니다.
Matijs

1
ie8 / 9에 대한 아이디어가 있습니까?
user151496

1
@ user151496 체크 아웃 : stackoverflow.com/revisions/…
HaNdTriX

90

상당히 간단한 해결책을 찾은 것 같습니다. 문제는 URL의 해시가 스크롤되는 페이지의 요소이기도하다는 것입니다. 텍스트를 해시 앞에 추가하면 더 이상 기존 요소를 참조하지 않습니다!

$(function(){
    //This emulates a click on the correct button on page load
    if(document.location.hash){
     $("#buttons li a").removeClass('selected');
     s=$(document.location.hash.replace("btn_","")).addClass('selected').attr("href").replace("javascript:","");
     eval(s);
    }

    //Click a button to change the hash
    $("#buttons li a").click(function(){
            $("#buttons li a").removeClass('selected');
            $(this).addClass('selected');
            document.location.hash="btn_"+$(this).attr("id")
            //return false;
    });
});

이제 URL이 page.aspx#btn_elementID페이지에서 실제 ID가 아닌 것으로 나타납니다 . "btn_"을 제거하고 실제 요소 ID를 얻습니다.


5
훌륭한 솔루션. 가장 고통스럽지 않습니다.
Swader

이미 어떤 이유로 page.aspx # elementId로부터 URL을 위해 최선을 다하고 있다면 당신은 당신의 모든 ID에 "btn_"이 기술과 앞에 추가를 반전시킬 수 있습니다
joshuahedlund

2
CSS에서 : target 의사 선택기를 사용하는 경우 작동하지 않습니다.
Jason T Featheringham

1
이 솔루션을 사랑하십시오! 우리는 AJAX 기반 탐색에 URL 해시를 사용하고 있으며 ID 앞에 /논리적 으로 보입니다.
Connell

3

원래 코드의 스 니펫 :

$("#buttons li a").click(function(){
    $("#buttons li a").removeClass('selected');
    $(this).addClass('selected');
    document.location.hash=$(this).attr("id")
});

이것을 다음으로 변경하십시오.

$("#buttons li a").click(function(e){
    // need to pass in "e", which is the actual click event
    e.preventDefault();
    // the preventDefault() function ... prevents the default action.
    $("#buttons li a").removeClass('selected');
    $(this).addClass('selected');
    document.location.hash=$(this).attr("id")
});

hash속성을 설정 하면 페이지가 스크롤 되기 때문에 작동하지 않습니다 .
jordanbtucker

3

나는 최근 window.location.hash상태를 유지하기 위해 의존하는 회전 목마를 만들고 있었고 Chrome 및 웹 키트 브라우저가 window.onhashchange이벤트가 시작될 때 어색한 멍청이와 스크롤을 강제로 표시합니다 (심지어 보이지 않는 대상까지) .

전파를 중지시키는 핸들러를 등록하려고 시도하는 경우에도 :

$(window).on("hashchange", function(e) { 
  e.stopPropogation(); 
  e.preventDefault(); 
});

기본 브라우저 동작을 중지하지 않았습니다. 내가 찾은 해결책 window.history.pushState은 바람직하지 않은 부작용을 유발하지 않고 해시를 변경하는 데 사용 되었습니다.

 $("#buttons li a").click(function(){
    var $self, id, oldUrl;

    $self = $(this);
    id = $self.attr('id');

    $self.siblings().removeClass('selected'); // Don't re-query the DOM!
    $self.addClass('selected');

    if (window.history.pushState) {
      oldUrl = window.location.toString(); 
      // Update the address bar 
      window.history.pushState({}, '', '#' + id);
      // Trigger a custom event which mimics hashchange
      $(window).trigger('my.hashchange', [window.location.toString(), oldUrl]);
    } else {
      // Fallback for the poors browsers which do not have pushState
      window.location.hash = id;
    }

    // prevents the default action of clicking on a link.
    return false;
});

그런 다음 일반적인 hashchange 이벤트와 다음을 모두들을 수 있습니다 my.hashchange.

$(window).on('hashchange my.hashchange', function(e, newUrl, oldUrl){
  // @todo - do something awesome!
});

물론 my.hashchange이벤트 이름의 예일뿐입니다.
최대

2

좋아, 이것은 다소 오래된 주제이지만 '올바른'답변이 CSS와 잘 작동하지 않기 때문에 칩을 넣을 것이라고 생각했습니다.

이 솔루션은 기본적으로 클릭 이벤트가 페이지를 이동하는 것을 방지 하므로 스크롤 위치를 먼저 얻을 수 있습니다. 그런 다음 우리는 수동으로 해시를 추가하고 브라우저가 자동으로 트리거 hashchange의 이벤트를. 우리는 hashchange 이벤트를 캡처하고 올바른 위치로 다시 스크롤합니다. 콜백은 해시 해킹을 한 곳에 유지하여 코드를 분리하여 지연을 일으키는 것을 방지합니다.

var hashThis = function( $elem, callback ){
    var scrollLocation;
    $( $elem ).on( "click", function( event ){
        event.preventDefault();
        scrollLocation = $( window ).scrollTop();
        window.location.hash = $( event.target ).attr('href').substr(1);
    });
    $( window ).on( "hashchange", function( event ){
        $( window ).scrollTop( scrollLocation );
        if( typeof callback === "function" ){
            callback();
        }
    });
}
hashThis( $( ".myAnchor" ), function(){
    // do something useful!
});

당신은 hashchange가 필요하지 않습니다, 그냥 뒤로 스크롤
daniel.gindi

2

음, 나는 다소 조잡하지만 확실히 작동하는 방법이 있습니다.
현재 스크롤 위치를 임시 변수에 저장 한 다음 해시를 변경 한 후 재설정하십시오. :)

원래 예제의 경우 :

$("#buttons li a").click(function(){
        $("#buttons li a").removeClass('selected');
        $(this).addClass('selected');

        var scrollPos = $(document).scrollTop();
        document.location.hash=$(this).attr("id")
        $(document).scrollTop(scrollPos);
});

이 방법의 문제점은 모바일 브라우저에서 스크롤이 수정되었음을 알 수 있습니다.
Tofandel

1

나는 이것이 가능하지 않다고 생각합니다. 내가 아는 한, 브라우저가 변경된 document.location.hash것으로 스크롤하지 않는 유일한 시간 은 해시가 페이지 내에 존재하지 않는 경우입니다.

이 기사 는 질문과 직접 ​​관련이 없지만 일반적인 브라우저 동작 변경에 대해 설명합니다.document.location.hash


1

해시 파서와 함께 hashchange 이벤트를 사용하는 경우 링크의 기본 동작을 방지하고 location.hash를 변경하여 한 문자를 추가하여 요소의 id 속성과 다릅니다

$('a[href^=#]').on('click', function(e){
    e.preventDefault();
    location.hash = $(this).attr('href')+'/';
});

$(window).on('hashchange', function(){
    var a = /^#?chapter(\d+)-section(\d+)\/?$/i.exec(location.hash);
});

완전한! 해시 리로드 문제를 해결하기 위해 5 시간이 지난 후 ... : / 이것이 작동 한 유일한 것입니다 !! 감사!!!
Igor Trindade

1

더 관련성이 높은 질문이 모두 여기를 가리키는 중복으로 표시되어 있기 때문에 여기에 추가하면…

내 상황이 더 간단합니다.

  • 사용자가 링크를 클릭하면 ( a[href='#something'])
  • 클릭 핸들러는 다음을 수행합니다. e.preventDefault()
  • 부드러운 스크롤 기능 : $("html,body").stop(true,true).animate({ "scrollTop": linkoffset.top }, scrollspeed, "swing" );
  • 그때 window.location = link;

이런 식으로 스크롤이 발생하며 위치가 업데이트 될 때 점프가 없습니다.


0

다른 방법은 뷰포트 상단에 숨겨진 div를 추가하는 것입니다. 이 div에는 해시가 URL에 추가되기 전에 해시 ID가 할당됩니다 .... 그래서 스크롤을 얻지 못합니다.


0

히스토리 사용 탭에 대한 솔루션은 다음과 같습니다.

    var tabContainer = $(".tabs"),
        tabsContent = tabContainer.find(".tabsection").hide(),
        tabNav = $(".tab-nav"), tabs = tabNav.find("a").on("click", function (e) {
                e.preventDefault();
                var href = this.href.split("#")[1]; //mydiv
                var target = "#" + href; //#myDiv
                tabs.each(function() {
                    $(this)[0].className = ""; //reset class names
                });
                tabsContent.hide();
                $(this).addClass("active");
                var $target = $(target).show();
                if ($target.length === 0) {
                    console.log("Could not find associated tab content for " + target);
                } 
                $target.removeAttr("id");
                // TODO: You could add smooth scroll to element
                document.location.hash = target;
                $target.attr("id", href);
                return false;
            });

마지막으로 선택한 탭을 표시하려면

var currentHashURL = document.location.hash;
        if (currentHashURL != "") { //a tab was set in hash earlier
            // show selected
            $(currentHashURL).show();
        }
        else { //default to show first tab
            tabsContent.first().show();
        }
        // Now set the tab to active
        tabs.filter("[href*='" + currentHashURL + "']").addClass("active");

을 참고 *=filter전화. 이것은 jQuery 관련 항목이며 기록이 없으면 기록 가능 탭이 실패합니다.


0

이 솔루션은 실제 scrollTop에 div를 만들고 해시를 변경 한 후 제거합니다.

$('#menu a').on('click',function(){
    //your anchor event here
    var href = $(this).attr('href');
    window.location.hash = href;
    if(window.location.hash == href)return false;           
    var $jumpTo = $('body').find(href);
    $('body').append(
        $('<div>')
            .attr('id',$jumpTo.attr('id'))
            .addClass('fakeDivForHash')
            .data('realElementForHash',$jumpTo.removeAttr('id'))
            .css({'position':'absolute','top':$(window).scrollTop()})
    );
    window.location.hash = href;    
});
$(window).on('hashchange', function(){
    var $fakeDiv = $('.fakeDivForHash');
    if(!$fakeDiv.length)return true;
    $fakeDiv.data('realElementForHash').attr('id',$fakeDiv.attr('id'));
    $fakeDiv.remove();
});

페이지로드시 앵커 이벤트를 트리거하는 옵션 :

$('#menu a[href='+window.location.hash+']').click();

0

나를 위해 더 간단한 방법이 있습니다. 기본적으로 해시가 실제로 HTML로 무엇인지 기억하십시오. 이름 태그에 대한 앵커 링크입니다. 그것이 스크롤되는 이유입니다 ... 브라우저가 앵커 링크로 스크롤하려고합니다. 그러니 하나주세요!

  1. BODY 태그 바로 아래에 다음 버전을 넣으십시오.
 <a name="home"> </a> <a name="firstsection"> </a> <a name="secondsection"> </a> <a name="thirdsection"> </a>
  1. ID 대신 클래스로 섹션 div 이름을 지정하십시오.

  2. 처리 코드에서 해시 마크를 제거하고 점으로 바꿉니다.

    var trimPanel = loadhash.substring (1); // 해시를 잃어버린다

    var dotSelect = '.' + trimPanel; // 점으로 해시 교체

    $ (dotSelect) .addClass ( "activepanel"). show (); // 해시와 관련된 div를 표시합니다.

마지막으로 element.preventDefault를 제거하거나 false를 반환하고 탐색을 수행하십시오. 창이 맨 위에 유지되고 해시가 주소 표시 줄 URL에 추가되고 올바른 패널이 열립니다.


0

해시 변경 전에 스크롤을 해당 위치로 재설정해야한다고 생각합니다.

$(function(){
    //This emulates a click on the correct button on page load
    if(document.location.hash) {
        $("#buttons li a").removeClass('selected');
        s=$(document.location.hash).addClass('selected').attr("href").replace("javascript:","");
        eval(s);
    }

    //Click a button to change the hash
    $("#buttons li a").click(function() {
            var scrollLocation = $(window).scrollTop();
            $("#buttons li a").removeClass('selected');
            $(this).addClass('selected');
            document.location.hash = $(this).attr("id");
            $(window).scrollTop( scrollLocation );
    });
});

0

페이지에서 id를 일종의 기준점으로 사용하고 사용자가 URL 끝에 #something을 추가하고 자신이 정의한 애니메이션을 사용하여 페이지를 해당 #something 섹션으로 스크롤하도록하려는 시나리오가있는 경우 자바 스크립트 함수, hashchange 이벤트 리스너는 그렇게 할 수 없습니다.

예를 들어 hashchange 이벤트 직후에 디버거를 넣으면 jquery를 사용하지만 요점을 알 수 있습니다.

$(window).on('hashchange', function(){debugger});

URL을 변경하고 Enter 버튼을 누르 자마자 페이지가 해당 섹션에서 즉시 중지되고 그 후에 만 ​​정의 된 스크롤 기능이 트리거되고 해당 섹션으로 스크롤됩니다. 아주 나쁜.

내 제안은 :

  1. 스크롤하려는 섹션의 앵커 포인트로 id를 사용하지 마십시오.

  2. 나처럼 ID를 사용해야한다면 대신 'popstate'이벤트 리스너를 사용하면 URL에 추가하는 바로 그 섹션으로 자동 스크롤되지 않고 대신 popstate 이벤트 내에서 자체 정의 된 함수를 호출 할 수 있습니다.

    $(window).on('popstate', function(){myscrollfunction()});

마지막으로 자신이 정의한 스크롤 기능에서 약간의 트릭을 수행해야합니다.

    let hash = window.location.hash.replace(/^#/, '');
    let node = $('#' + hash);
    if (node.length) {
        node.attr('id', '');
    }
    if (node.length) {
        node.attr('id', hash);
    }

태그에서 ID를 삭제하고 재설정하십시오.

이것은 트릭을해야합니다.


-5

이 코드를 문서 준비시 jQuery에만 추가하십시오.

참조 : http://css-tricks.com/snippets/jquery/smooth-scrolling/

$(function() {
  $('a[href*=#]:not([href=#])').click(function() {
    if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
      var target = $(this.hash);
      target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
      if (target.length) {
        $('html,body').animate({
          scrollTop: target.offset().top
        }, 1000);
        return false;
      }
    }
  });
});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.