Как я могу обновить window.location.hash, не перепрыгивая документ?

163

У меня есть скользящая панель на моем сайте.

Когда он закончил анимацию, я установил хеш так

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

(это обратный вызов, и idон назначается ранее).

Это хорошо работает, чтобы позволить пользователю добавить закладку на панель, а также для работы без версии JavaScript.

Однако, когда я обновляю хеш, браузер переходит на место. Я предполагаю, что это ожидаемое поведение.

Мой вопрос: как я могу предотвратить это? Т.е. как я могу изменить хеш окна, но не прокручивать браузер до элемента, если хеш существует? Что-то в этом event.preventDefault()роде?

Я использую jQuery 1.4 и плагин scrollTo .

Большое спасибо!

Обновить

Вот код, который меняет панель.

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

            }
    });
});
Алекс
источник
2
Я полагаю, вы пытались event.preventDefault():)
Марко
@ Марко, я не знаю, где его разместить!
Алекс
Можете ли вы опубликовать остальную часть своего кода?
Марко
@ Марко Ивановский: Не думаю, что это актуально, но я посмотрю, что я могу сделать.
Алекс
3
@ Гарет Я не думаю, что есть место для этого, потому что это происходит, как только я обновляю хэш.
Алекс

Ответы:

261

Существует обходной путь с использованием API истории в современных браузерах с резервным вариантом на старых:

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

Кредит идет к Леа Веру

Аттила Фулоп
источник
Это лучший ответ, на мой взгляд.
Грег Аннандейл
10
Обратите внимание, что pushState имеет побочный (с отступом) эффект добавления состояния в стек истории браузера. Другими словами, когда пользователь нажимает кнопку «Назад», ничего не произойдет, если вы также не добавите прослушиватель события popstate.
Дэвид Кук
32
@DavidCook - Мы также могли бы использовать history.replaceState(что для изменений хеша может иметь больше смысла), чтобы избежать необходимости в popstateпрослушивателе событий.
Джек
Это сработало для меня. Мне нравится эффект изменения истории. Я хочу добавить предостережение, что это не вызовет hashchangeсобытие. Это было то, что я должен был обойти.
Джордан
предупреждение! это не вызовет hashchangeсобытие
Сантьяго Аристи
52

Проблема в том, что вы устанавливаете window.location.hash для атрибута ID элемента. Предполагается, что браузер будет переходить к этому элементу независимо от того, используете ли вы «protectDefault ()» или нет.

Одним из способов обойти это является добавление к хешу произвольного значения, например:

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

Затем все, что вам нужно сделать, это проверить хэш с префиксом при загрузке страницы. В качестве дополнительного бонуса вы можете даже плавно прокрутить его, так как теперь вы контролируете значение хэша ...

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

Если вам нужно, чтобы это работало постоянно (а не только при начальной загрузке страницы), вы можете использовать функцию для отслеживания изменений в значении хеш-кода и быстрого перехода к нужному элементу:

var foundHash;
setInterval(function() {
    var h = window.location.hash.replace('panel-', '');
    if (h && h !== foundHash) {
        $('#slider').scrollTo(h, 800);
        foundHash = h;
    }
}, 100);
Дерек Хунзикер
источник
2
Это единственное решение, которое фактически отвечает на вопрос - разрешить хэширование без прыжков
amosmos
Это действительно отвечает на вопрос, объясняющий добавление и удаление произвольного значения для хэша, чтобы избежать перехода в соответствии с поведением браузера по умолчанию.
lowtechsun
1
хорошее решение, но ослабляет решение OPs, поскольку оно сводит на нет использование секций закладок
Samus
27

Дешевое и неприятное решение .. Используйте # уродливый! стиль.

Чтобы установить это:

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

Чтобы прочитать это:

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

Так как он не совпадает с привязкой или идентификатором на странице, он не будет прыгать.

Гэвин Брок
источник
3
Мне пришлось сохранить совместимость с IE 7 и некоторыми мобильными версиями сайта. Это решение было самым чистым для меня. Вообще я был бы во всех решениях pushState.
Эрик Гудвин
1
установив хеш с помощью: window.location.hash = '#/' + id;и затем заменив его на: window.location.hash.replace(/^#\//, '#');немного прертируем URL -> projects/#/tab1
braitsch
13

Почему бы вам не получить текущую позицию прокрутки, поместить ее в переменную, затем назначить хэш и вернуть прокрутку страницы туда, где она была:

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

это должно работать

user706270
источник
1
Хм, это работало для меня некоторое время, но когда-то в последние несколько месяцев это перестало работать на Firefox ....
Matchew
10

Я использовал комбинацию решения Attila Fulop (Lea Verou) для современных браузеров и решения Gavin Brock для старых браузеров следующим образом:

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

Как заметил Гэвин Брок, для получения идентификатора вам нужно будет обработать строку (которая в этом случае может иметь или не иметь «!») Следующим образом:

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

До этого я пробовал решение, подобное предложенному пользователем 706270, но оно не работало с Internet Explorer: поскольку его движок Javascript не очень быстрый, вы можете заметить увеличение и уменьшение прокрутки, что приводит к неприятному визуальному эффекту.

aldemarcalazans
источник
2

Это решение сработало для меня.

Проблема с настройкой location.hashзаключается в том, что страница перейдет к этому идентификатору, если он найден на странице.

Проблема в window.history.pushStateтом, что он добавляет запись в историю для каждой вкладки, которую нажимает пользователь. Затем, когда пользователь нажимаетback кнопку, они переходят на предыдущую вкладку. (это может или не может быть то, что вы хотите. это было не то, что я хотел).

Для меня replaceStateлучшим вариантом было то, что он заменяет только текущую историю, поэтому, когда пользователь нажимает backкнопку, он переходит на предыдущую страницу.

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

Ознакомьтесь с документацией по истории API на MDN.

Майк Валлано
источник
1

Я не уверен, что вы можете изменить исходный элемент, но как насчет переключения с использования идентификатора attr на что-то другое, например data-id? Затем просто прочитайте значение data-id для вашего хеш-значения, и оно не будет прыгать.

Джон Б
источник
1

Это решение сработало для меня

// 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');

И мой полный код JS

$('#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');
Рохит Дхиман
источник
0

При использовании фреймворка laravel у меня были некоторые проблемы с использованием функции route-> back (), так как она стерла мой хэш. Чтобы сохранить мой хэш, я создал простую функцию:

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

и я установил это в моей другой функции JS следующим образом:

localStorage.setItem("hash", myvalue);

Вы можете назвать ваши значения локального хранилища так, как вам нравится; шахта им hash.

Поэтому, если хэш установлен на PAGE1, а затем вы переходите к PAGE2; хеш будет воссоздан на PAGE1, когда вы нажмете Back на PAGE2.

Андрей
источник