Bagaimana saya bisa memperbarui window.location.hash tanpa melompati dokumen?

163

Saya sudah memasang panel geser di situs web saya.

Ketika selesai animasi, saya mengatur hash seperti itu

function() {
   window.location.hash = id;
}

(ini adalah panggilan balik, dan idditugaskan sebelumnya).

Ini berfungsi baik, untuk memungkinkan pengguna untuk menandai panel, dan juga untuk versi non JavaScript untuk bekerja.

Namun, ketika saya memperbarui hash, browser melompat ke lokasi. Saya kira ini adalah perilaku yang diharapkan.

Pertanyaan saya adalah: bagaimana saya bisa mencegah ini? Yaitu bagaimana saya bisa mengubah hash jendela, tetapi tidak memiliki browser gulir ke elemen jika hash ada? Semacam event.preventDefault()itu?

Saya menggunakan jQuery 1.4 dan plugin scrollTo .

Terimakasih banyak!

Memperbarui

Berikut adalah kode yang mengubah panel.

$('#something a').click(function(event) {
    event.preventDefault();
    var link = $(this);
    var id = link[0].hash;

    $('#slider').scrollTo(id, 800, {
        onAfter: function() {

            link.parents('li').siblings().removeClass('active');
            link.parent().addClass('active');
            window.location.hash = id;

            }
    });
});
alex
sumber
2
Saya berasumsi Anda sudah mencoba event.preventDefault():)
Marko
@ Marko, saya tidak tahu di mana harus meletakkannya!
alex
Bisakah Anda memposting sisa kode Anda?
Marko
@ Marko Ivanovski Saya tidak berpikir itu relevan, tetapi saya akan melihat apa yang bisa saya lakukan.
alex
3
@ Gareth Saya tidak berpikir ada tempat untuk itu, karena itu terjadi segera setelah saya memperbarui hash.
alex

Jawaban:

261

Ada solusi dengan menggunakan API riwayat di peramban modern dengan mundur pada yang lama:

if(history.pushState) {
    history.pushState(null, null, '#myhash');
}
else {
    location.hash = '#myhash';
}

Kredit diberikan kepada Lea Verou

Attila Fulop
sumber
Ini jawaban yang lebih baik, menurut saya.
Greg Annandale
10
Perhatikan bahwa pushState memiliki efek samping (indentasi) menambahkan status ke tumpukan riwayat browser. Dengan kata lain, ketika pengguna mengklik tombol kembali, tidak ada yang terjadi kecuali Anda juga menambahkan pendengar acara popstate.
David Cook
32
@ Davidvid - Kita juga bisa menggunakan history.replaceState(yang, untuk perubahan hash, mungkin lebih masuk akal) untuk menghindari kebutuhan popstatependengar acara.
Jack
Ini berhasil untuk saya. Saya suka efek dari perubahan sejarah. Saya ingin menambahkan peringatan bahwa ini tidak akan memicu hashchangeacara. Itu adalah sesuatu yang harus saya atasi.
Jordan
peringatan! ini tidak akan memicu hashchangeacara
santiago arizti
52

Masalahnya adalah Anda mengatur window.location.hash ke atribut ID elemen. Ini adalah perilaku yang diharapkan untuk browser untuk melompat ke elemen itu, terlepas dari apakah Anda "mencegahDefault ()" atau tidak.

Salah satu cara untuk mengatasi ini adalah dengan awalan hash dengan nilai arbitrary seperti:

window.location.hash = 'panel-' + id.replace('#', '');

Kemudian, yang perlu Anda lakukan adalah memeriksa hash yang diawali pada pemuatan halaman. Sebagai bonus tambahan, Anda bahkan dapat menggulirkannya dengan lancar karena Anda sekarang mengendalikan nilai hash ...

$(function(){
    var h = window.location.hash.replace('panel-', '');
    if (h) {
        $('#slider').scrollTo(h, 800);
    }
});

Jika Anda ingin ini berfungsi setiap saat (dan tidak hanya pada pemuatan halaman awal), Anda dapat menggunakan fungsi untuk memantau perubahan pada nilai hash dan melompat ke elemen yang benar dengan cepat:

var foundHash;
setInterval(function() {
    var h = window.location.hash.replace('panel-', '');
    if (h && h !== foundHash) {
        $('#slider').scrollTo(h, 800);
        foundHash = h;
    }
}, 100);
Derek Hunziker
sumber
2
Ini adalah satu-satunya solusi yang benar-benar menjawab pertanyaan - memungkinkan hashing tanpa melompat
amosmos
Ini benar-benar menjawab pertanyaan yang menjelaskan penambahan dan penghapusan nilai arbitrer untuk hash untuk menghindari lompatan sesuai perilaku browser default.
lowtechsun
1
solusi yang baik, tetapi melemahkan solusi OPs karena meniadakan penggunaan bagian bookmark
Samus
27

Solusi murah dan tidak menyenangkan .. Gunakan # yang jelek! gaya.

Untuk mengaturnya:

window.location.hash = '#!' + id;

Untuk membacanya:

id = window.location.hash.replace(/^#!/, '');

Karena tidak cocok dan berlabuh atau id di halaman, itu tidak akan melompat.

Gavin Brock
sumber
3
Saya harus menjaga kompatibilitas dengan IE 7 dan beberapa versi seluler situs tersebut. Solusi ini terbersih untuk saya. Secara umum saya akan seluruh solusi pushState.
Eric Goodwin
1
mengatur hash dengan: window.location.hash = '#/' + id;dan kemudian menggantinya dengan: window.location.hash.replace(/^#\//, '#');akan sedikit projects/#/tab1
memperindah
13

Mengapa Anda tidak mendapatkan posisi gulir saat ini, memasukkannya ke dalam variabel kemudian menetapkan hash dan meletakkan halaman gulir kembali ke tempat itu:

var yScroll=document.body.scrollTop;
window.location.hash = id;
document.body.scrollTop=yScroll;

ini seharusnya bekerja

pengguna706270
sumber
1
hm, ini bekerja untuk saya untuk sementara waktu, tetapi kadang-kadang dalam beberapa bulan terakhir ini berhenti bekerja pada firefox ....
cocok
10

Saya menggunakan kombinasi solusi Attila Fulop (Lea Verou) untuk browser modern dan solusi Gavin Brock untuk browser lama sebagai berikut:

if (history.pushState) {
    // IE10, Firefox, Chrome, etc.
    window.history.pushState(null, null, '#' + id);
} else {
    // IE9, IE8, etc
    window.location.hash = '#!' + id;
}

Seperti yang diamati oleh Gavin Brock, untuk mendapatkan kembali id ​​Anda harus memperlakukan string (yang dalam hal ini dapat memiliki atau tidak "!") Sebagai berikut:

id = window.location.hash.replace(/^#!?/, '');

Sebelum itu, saya mencoba solusi yang mirip dengan yang diusulkan oleh user706270, tetapi tidak bekerja dengan baik dengan Internet Explorer: karena mesin Javascript-nya tidak terlalu cepat, Anda dapat melihat peningkatan dan penurunan gulir, yang menghasilkan efek visual yang buruk.

aldemarcalazans
sumber
2

Solusi ini bekerja untuk saya.

Masalah dengan pengaturan location.hashadalah bahwa halaman akan melompat ke id jika ditemukan di halaman.

Masalahnya window.history.pushStateadalah ia menambahkan entri ke riwayat untuk setiap tab yang diklik pengguna. Kemudian ketika pengguna mengklik backtombol, mereka pergi ke tab sebelumnya. (ini mungkin atau mungkin bukan yang Anda inginkan. bukan itu yang saya inginkan).

Bagi saya, replaceStateadalah pilihan yang lebih baik karena hanya mengganti riwayat saat ini, jadi ketika pengguna mengklik backtombol, mereka pergi ke halaman sebelumnya.

$('#tab-selector').tabs({
  activate: function(e, ui) {
    window.history.replaceState(null, null, ui.newPanel.selector);
  }
});

Lihat dokumentasi API Sejarah di MDN.

Mike Vallano
sumber
1

Saya tidak yakin apakah Anda dapat mengubah elemen asli tetapi bagaimana dengan beralih dari menggunakan id attr ke sesuatu yang lain seperti data-id? Kemudian cukup baca nilai data-id untuk nilai hash Anda dan itu tidak akan melompat.

Jon B
sumber
1

Solusi ini berhasil untuk saya

// store the currently selected tab in the hash value
    if(history.pushState) {
        window.history.pushState(null, null, '#' + id);
    }
    else {
        window.location.hash = id;
    }

// on load of the page: switch to the currently selected tab
var hash = window.location.hash;
$('#myTab a[href="' + hash + '"]').tab('show');

Dan kode js lengkap saya adalah

$('#myTab a').click(function(e) {
  e.preventDefault();
  $(this).tab('show');
});

// store the currently selected tab in the hash value
$("ul.nav-tabs > li > a").on("shown.bs.tab", function(e) {
  var id = $(e.target).attr("href").substr(1);
    if(history.pushState) {
        window.history.pushState(null, null, '#' + id);
    }
    else {
        window.location.hash = id;
    }
   // window.location.hash = '#!' + id;
});

// on load of the page: switch to the currently selected tab
var hash = window.location.hash;
// console.log(hash);
$('#myTab a[href="' + hash + '"]').tab('show');
Rohit Dhiman
sumber
0

Saat menggunakan framework laravel, saya memiliki beberapa masalah dengan menggunakan fungsi route-> back () karena menghapus hash saya. Untuk menjaga hash saya, saya membuat fungsi sederhana:

$(function() {   
    if (localStorage.getItem("hash")    ){
     location.hash = localStorage.getItem("hash");
    }
}); 

dan saya mengaturnya di fungsi JS saya yang lain seperti ini:

localStorage.setItem("hash", myvalue);

Anda dapat memberi nama nilai penyimpanan lokal Anda sesuka Anda; nama saya hash.

Karena itu, jika hash diatur pada PAGE1 dan kemudian Anda menavigasi ke PAGE2; hash akan dibuat kembali pada PAGE1 ketika Anda mengklik Kembali pada PAGE2.

Andrew
sumber