Изменение документа.местоположение.хэш без прокрутки страницы
У нас есть несколько страниц, использующих ajax для загрузки контента, и есть несколько случаев, когда нам нужно глубоко связать страницу. Вместо того, чтобы иметь ссылку на " пользователей "и говорить людям нажать" Настройки", полезно иметь возможность связывать людей с пользователей.aspx # settings
чтобы люди могли предоставить нам правильные ссылки на разделы (для технической поддержки и т. д.) Я настроил его для автоматического изменения хэша в 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;
остановит прокрутку страницы - но это просто делает ссылку не работает вообще. Так что это просто прокомментировал на данный момент, так что я могу перемещаться.
какие идеи?
17 ответов:
Шаг 1: вам нужно разрядить идентификатор узла, пока хэш не будет установлен. Это делается путем удаления идентификатора с узла во время установки хэша, а затем добавления его обратно.
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'D, поэтому вам нужно немного помочь им. Вам нужно добавить дополнительный
div
в верхней части окна просмотра установите его идентификатор в хэш, а затем откатите все назад: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: оберните его в a плагин и использовать его вместо того, чтобы писать в
location.hash
...
Я думаю, я нашел довольно простое решение. Проблема в том, что хэш в 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
который не является реальным идентификатором на странице. Я просто удаляю "btn_" и получаю фактический идентификатор элемента
использовать
history.replaceState
илиhistory.pushState
* для изменения хэша. Это не вызовет переход к связанному элементу.пример
$(document).on('click', 'a[href^=#]', function(event) { event.preventDefault(); history.pushState({}, '', this.href); });
* если вы хотите историю вперед и назад поддержка
история поведения
если вы используете
history.pushState
и вы не хотите прокрутки страницы, когда пользователь использует кнопки истории браузера (вперед/назад) проверить из экспериментальнойscrollRestoration
задание (только Chrome 46+).history.scrollRestoration = 'manual';
Поддержка Браузеров
фрагмент исходного кода:
$("#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") });
Я недавно строил карусель, которая опирается на
window.location.hash
для поддержания состояния и сделал открытие, что браузер Chrome и webkit заставит прокрутку (даже до невидимой цели) с неудобным рывком, когда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! });
хорошо, это довольно старая тема, но я думал, что скинусь, поскольку "правильный" ответ не работает хорошо с 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! });
У меня есть несколько грубый, но определенно рабочий метод.
Просто сохраните текущую позицию прокрутки в переменной temp, а затем сбросьте ее после изменения хэша. :)Итак, для исходного примера:
$("#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); });
Я не думаю, что это возможно. Насколько я знаю, единственный раз, когда браузер не прокручивается до измененного
document.location.hash
если хэш не существует на странице.в этой статье не имеет прямого отношения к вашему вопросу, но он обсуждает типичное поведение браузера при изменении
document.location.hash
Если вы используете событие hashchange с hash parser, вы можете предотвратить действие по умолчанию на ссылки и изменить местоположение.хэш добавление одного символа, чтобы иметь разницу со свойством 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); });
добавление этого здесь, потому что более актуальные вопросы были отмечены как дубликаты, указывающие здесь...
моя ситуация проще:
- пользователь нажимает на ссылку (
a[href='#something']
)- нажмите кнопку обработчик выполняет:
e.preventDefault()
- функция smoothscroll:
$("html,body").stop(true,true).animate({ "scrollTop": linkoffset.top }, scrollspeed, "swing" );
- затем
window.location = link;
таким образом, прокрутка происходит, и нет никакого перехода, когда местоположение обновляется.
другой способ сделать это-добавить div, который скрыт в верхней части окна просмотра. Затем этому div присваивается идентификатор хэша, прежде чем хэш будет добавлен в url....so тогда вы не получите свиток.
вот мое решение для вкладок с поддержкой истории:
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 вещь, и без нее ваши вкладки с поддержкой истории не будут работать.
это решение создает div на фактическом scrollTop и удаляет его после изменения хэша:
$('#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();
у меня есть более простой способ, который работает для меня. В принципе, помните, что хэш на самом деле находится в HTML. Это якорная ссылка на имя тега. Вот почему он прокручивается...браузер пытается перейти к ссылке привязки. Так, дайте ему один!
- прямо под тегом BODY, поместите свою версию этого:
<a name="home"></a><a name="firstsection"></a><a name="secondsection"></a><a name="thirdsection"></a>
Назовите свой раздел divs с классами вместо идентификаторов.
в вашем коде обработки, снимите хэш отметить и заменить точкой:
var trimPanel = loadhash.substring(1); //lose the hash var dotSelect = '.' + trimPanel; //replace hash with dot $(dotSelect).addClass("activepanel").show(); //show the div associated with the hash.наконец, удалить элемент.preventDefault или return: false и разрешить навигацию. Окно останется вверху, хэш будет добавлен к url адресной строки, и откроется правильная панель.
Я думаю, что вам нужно сбросить прокрутку до своей позиции перед hashchange.
$(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 ); }); });
Если на Вашей странице вы используете id как своего рода якорную точку, и у вас есть сценарии, в которых вы хотите, чтобы пользователи добавляли #что-то в конец url-адреса и прокручивали страницу до этого #что-то раздел, используя свою собственную определенную анимированную функцию javascript, прослушиватель событий hashchange не сможет этого сделать.
Если вы просто ставите отладчик сразу после события hashchange, например, что-то вроде этого(ну, я использую jquery, но вы получаете точка):
$(window).on('hashchange', function(){debugger});
вы заметите, что как только вы измените свой url и нажмете кнопку enter, страница сразу же остановится в соответствующем разделе, только после этого ваша собственная определенная функция прокрутки будет активирована, и она будет прокручиваться в этот раздел, что выглядит очень плохо.
мое предложение:
не использовать ID в качестве точки привязки к разделу, который вы хотите выбрать.
Если вы должны используйте 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); }
удалить идентификатор на теге и сбросить его.
Это должно сделать трюк.
только добавьте этот код в jQuery на document ready
Ref: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; } } }); });