Sayfa kaydırmadan location.hash'i değiştirme


153

İçeriği yüklemek için ajax kullanan birkaç sayfamız var ve bir sayfaya derin bağlantı vermemiz gereken birkaç durum var. "Kullanıcılar" bağlantısına sahip olmak ve insanlara "ayarlar" ı tıklamalarını söylemek yerine, kişileri user.aspx # ayarlarına bağlayabilmek yararlıdır.

İnsanların bize bölümlere doğru bağlantılar sağlamasına izin vermek için (teknik destek vb. İçin), bir düğmeye her tıklandığında URL'deki karmayı otomatik olarak değiştirecek şekilde ayarladım. Tabii ki tek sorun, bu olduğunda, sayfayı da bu öğeye kaydırmasıdır.

Bunu devre dışı bırakmanın bir yolu var mı? Bunu şimdiye kadar nasıl yaptığımı aşağıda bulabilirsiniz

$(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;Sayfanın kaydırılmasını durduracağını ummuştum - ancak bu, bağlantının hiç çalışmamasına neden oluyor. Bu şimdilik sadece yorumlandı, böylece gezinebilirim.

Herhangi bir fikir?

Yanıtlar:


111

Adım 1: Hash ayarlanana kadar düğüm kimliğini etkisiz hale getirmeniz gerekir. Bu, karma ayarlanırken kimliği düğümden kaldırarak ve ardından tekrar ekleyerek yapılır.

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

Adım 2: Bazı tarayıcılar, kimliklendirilmiş düğümün en son görüldüğü yere göre kaydırmayı tetikleyecektir, bu nedenle onlara biraz yardım etmeniz gerekir. divGörüntü alanının üstüne fazladan bir tane eklemeniz , kimliğini hash olarak ayarlamanız ve ardından her şeyi geri almanız gerekir :

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. Adım: Bir eklentiye sarın ve yazmak yerine bunu kullanın location.hash...


1
Hayır, 2. adımda olan şey, gizli bir div oluşturulması ve kaydırmanın mevcut konumuna yerleştirilmesidir. Görsel olarak konum: sabit / üst: 0 ile aynıdır. Böylece kaydırma çubuğu şu anda bulunduğu noktaya "taşınır".
Borgar

3
Bu çözüm gerçekten iyi çalışıyor - ancak satır: top: $ .scroll (). Top + 'px' Olmalıdır: top: $ (window) .scrollTop () + 'px'
Mark Perkins

27
Burada hangi tarayıcıların 2. adıma ihtiyacı olduğunu bilmek faydalı olacaktır.
djc

1
Teşekkürler Borgar. Hedef düğümün kimliğini silmeden önce fx div eklemeniz beni şaşırtıyor. Bu, belgede yinelenen kimliklerin olduğu bir an olduğu anlamına gelir. Potansiyel bir sorun veya en azından kötü davranış gibi görünüyor;)
Ben

1
Çok teşekkür ederim, bu da bana yardımcı oldu. Ancak, bu işlem sırasında bir yan etki de fark ettim: Genellikle "orta fare düğmesini" kilitleyerek ve fareyi kaydırmak istediğim yöne doğru hareket ettirerek kaydırırım. Google Chrome'da bu, kaydırma akışını kesintiye uğratır. Bunun için süslü bir çözüm var mı?
YMMD

103

Karmayı değiştirmek için history.replaceStateveya history.pushState* kullanın . Bu, ilişkili öğeye atlamayı tetiklemeyecektir.

Misal

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

JSFiddle Demosu

* Geçmişi ileri ve geri almak istiyorsanız

Geçmiş davranışı

Kullanıyorsanız history.pushStateve kullanıcı tarayıcının geçmiş düğmelerini (ileri / geri) kullandığında sayfanın kaydırılmasını istemiyorsanız, deneysel scrollRestorationayarı kontrol edin (yalnızca Chrome 46+) .

history.scrollRestoration = 'manual';

Tarayıcı Desteği


6
replaceStatemuhtemelen buraya gitmek için daha iyi bir yoldur. Varlık farkı pushState, geçmişinize bir öğe eklerken replaceStateeklemez.
Aakil Fernandes

Teşekkürler @AakilFernandes haklısınız. Cevabı yeni güncelledim.
HaNdTriX

Her zaman bodykayan şey değil , bazen de documentElement. Bu özü görün , Diego Perini
Matijs

1
ie8 / 9 hakkında bir fikriniz var mı?
user151496

1
@ user151496 kontrol edin: stackoverflow.com/revisions/…
HaNdTriX

90

Sanırım oldukça basit bir çözüm bulmuş olabilirim. Sorun şu ki, URL'deki karma aynı zamanda kaydırdığınız sayfadaki bir öğedir. hash'in başına biraz metin eklersem, artık mevcut bir öğeye başvurmuyor!

$(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;
    });
});

Artık URL page.aspx#btn_elementID, sayfada gerçek bir kimlik olmayan olarak görünür . Sadece "btn_" yi kaldırıyorum ve gerçek öğe kimliğini alıyorum


5
Harika çözüm. Çoğu acısız.
Swader

Herhangi bir nedenle page.aspx # elementID URL'lerini zaten taahhüt ettiyseniz, bu tekniği tersine çevirebilir ve tüm kimliklerinizin başına "btn_" ekleyebilirsiniz
joshuahedlund

2
Kullanıyorsanız çalışmaz: CSS'de sözde seçicileri hedefleyin.
Jason T Featheringham

1
Bu çözümü seviyorum! URL karmasını AJAX tabanlı gezinme için kullanıyoruz, kimliklerin başına /mantıklı görünüyor.
Connell

3

Orijinal kodunuzun bir parçası:

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

Bunu şu şekilde değiştirin:

$("#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")
});

Bu işe yaramaz çünkü hashözelliği ayarlamak sayfanın yine de kaymasına neden olur.
jordanbtucker

3

Geçenlerde window.location.hashdurumu korumaya dayanan bir döngü oluşturuyordum ve Chrome ve webkit tarayıcılarının, window.onhashchangeolay tetiklendiğinde garip bir sarsıntıyla kaydırmayı (görünmeyen bir hedefe bile) zorlayacağını keşfettim .

Yayılmayı durduran bir işleyiciyi kaydetmeye çalışırken bile:

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

Varsayılan tarayıcı davranışını durdurmak için hiçbir şey yapmadı. Bulduğum çözüm window.history.pushState, istenmeyen yan etkileri tetiklemeden karmayı değiştirmek için kullanmaktı .

 $("#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;
});

Daha sonra hem normal hashchange olayını hem de şunları dinleyebilirsiniz my.hashchange:

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

elbette my.hashchangesadece örnek bir etkinlik adı
en fazla

2

Tamam, bu oldukça eski bir konu ama 'doğru' cevap CSS ile iyi çalışmadığı için ekleyeceğimi düşündüm.

Bu çözüm temelde tıklama olayının sayfayı hareket ettirmesini engeller , böylece önce kaydırma konumunu alabiliriz. Ardından hash'i manuel olarak ekleriz ve tarayıcı otomatik olarak bir hashchange olayını tetikler . Hashchange olayını yakalar ve doğru konuma geri döneriz. Geri arama, hash hacklemenizi tek bir yerde tutarak kodunuzu ayırır ve gecikmeye neden olmasını önler.

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'e ihtiyacınız yok, hemen geri gidin
daniel.gindi 18'14

2

Biraz kaba ama kesinlikle işe yarayan bir yöntemim var.
Sadece geçerli kaydırma konumunu geçici bir değişkene kaydedin ve ardından karmayı değiştirdikten sonra sıfırlayın. :)

Orijinal örnek için:

$("#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);
});

Bu yöntemle ilgili sorun, Mobil Tarayıcılarda kaydırmanın değiştirildiğini fark edebilirsiniz
Tofandel

1

Bunun mümkün olduğunu sanmıyorum. Bildiğim kadarıyla, bir tarayıcının bir değişime kaydırmadığı tek zaman document.location.hash, karmanın sayfada mevcut olmamasıdır.

Bu makale doğrudan sorunuzla ilgili değildir, ancak değişen tipik tarayıcı davranışını tartışır.document.location.hash


1

hash parser ile hashchange olayını kullanırsanız, bağlantılarda varsayılan eylemi engelleyebilir ve location.hash öğesini bir karakter ekleyerek bir öğenin id özelliği ile farklı olacak şekilde değiştirebilirsiniz.

$('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);
});

Mükemmel! Hash yeniden yüklemeyle ilgili sorunu çözmeye çalıştıktan beş saat sonra ...: / Bu işe yarayan tek şeydi !! Teşekkürler!!!
Igor Trindade

1

Bunu buraya eklemek, çünkü daha alakalı soruların tümü burayı işaret eden kopyalar olarak işaretlendi ...

Durumum daha basit:

  • kullanıcı bağlantıyı tıklar ( a[href='#something'])
  • tıklama işleyicisi şunları yapar: e.preventDefault()
  • smoothscroll işlevi: $("html,body").stop(true,true).animate({ "scrollTop": linkoffset.top }, scrollspeed, "swing" );
  • sonra window.location = link;

Bu şekilde kaydırma gerçekleşir ve konum güncellendiğinde atlama olmaz.


0

Bunu yapmanın diğer yolu, görüntü alanının en üstünde gizli olan bir div eklemektir. Daha sonra bu div, hash url'ye eklenmeden önce hash'in kimliği atanır .... böylece bir kaydırma almazsınız.


0

Geçmişin etkin olduğu sekmeler için çözümüm:

    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;
            });

Ve son seçilen sekmeyi göstermek için:

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");

Not *=üzerinde filterçağrı. Bu jQuery'ye özgü bir şeydir ve onsuz, geçmiş etkin sekmeleriniz başarısız olur.


0

Bu çözüm, gerçek scrollTop'ta bir div oluşturur ve karmayı değiştirdikten sonra onu kaldırır:

$('#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();
});

isteğe bağlı, sayfa yüklemede bağlantı olayını tetikleme:

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

0

Benim için çalışan daha basit bir yöntemim var. Temel olarak, hash'in HTML'de gerçekte ne olduğunu hatırlayın. Bu, bir Ad etiketine bağlantı bağlantısıdır. Bu yüzden kaydırılıyor ... tarayıcı bir bağlantı bağlantısına gitmeye çalışıyor. Öyleyse, bir tane ver!

  1. BODY etiketinin hemen altına, bunun sürümünüzü girin:
 <a name="home"> </a> <a name="firstsection"> </a> <a name="secondsection"> </a> <a name="thirdsection"> </a>
  1. Bölüm div'lerinizi kimlikler yerine sınıflarla adlandırın.

  2. İşleme kodunuzda, karma işaretini çıkarın ve bir nokta ile değiştirin:

    var trimPanel = loadhash.substring (1); // karmayı kaybet

    var dotSelect = '.' + trimPanel; // hash yerine nokta koy

    $ (dotSelect) .addClass ("activepanel"). show (); // karma ile ilişkili div'i göster.

Son olarak, element.preventDefault veya return: false öğesini kaldırın ve nav'ın gerçekleşmesine izin verin. Pencere üstte kalacak, hash adres çubuğu url'sine eklenecek ve doğru panel açılacaktır.


0

Hashchange'den önce kaydırmayı konumuna sıfırlamanız gerektiğini düşünüyorum.

$(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

Sayfanızda bir tür bağlantı noktası olarak id kullanıyorsanız ve kullanıcıların URL'nin sonuna #something eklemesini ve sayfanın kendi tanımlı animasyonunuzu kullanarak o #something bölümüne kaydırmasını istediğiniz senaryolarınız varsa javascript işlevi, hashchange olay dinleyicisi bunu yapamaz.

Örneğin, hashchange olayından hemen sonra bir hata ayıklayıcı koyarsanız, örneğin, bunun gibi bir şey (iyi, ben jquery kullanıyorum, ama sen anladın):

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

URL'nizi değiştirip enter düğmesine bastığınızda, sayfanın ilgili bölümde hemen durduğunu, ancak bundan sonra, kendi tanımlı kaydırma işlevinizin tetikleneceğini ve bu bölüme kaydırıldığını fark edeceksiniz. çok kötü.

Benim önerim şu:

  1. tutturucu olarak kaydırmak istediğiniz bölüme işaret etmek için id kullanmayın.

  2. Benim yaptığım gibi, kimlik kullanmanız gerekiyorsa. Bunun yerine 'popstate' olay dinleyicisini kullanın, url'ye eklediğiniz bölüme otomatik olarak kaymaz, bunun yerine popstate olayı içinde kendi tanımlı işlevinizi çağırabilirsiniz.

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

Son olarak, kendi tanımlı kaydırma işlevinizde biraz hile yapmanız gerekir:

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

etiketinizdeki kimliği silin ve sıfırlayın.

Bu hile yapmalı.


-5

Bu kodu yalnızca belge hazır olduğunda jQuery'ye ekleyin

Referans: 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;
      }
    }
  });
});
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.