Я пытаюсь очистить работу своих якорей. У меня есть заголовок, который фиксируется в верхней части страницы, поэтому, когда вы ссылаетесь на привязку в другом месте страницы, страница перескакивает, поэтому якорь находится в верхней части страницы, оставляя содержимое за фиксированным заголовком (я надеюсь в этом есть смысл). Мне нужен способ смещения якоря на 25 пикселей с высоты заголовка. Я бы предпочел HTML или CSS, но Javascript также был бы приемлемым.
Компенсировать привязку html для настройки для фиксированного заголовка
Ответ 1
Вы можете просто использовать CSS без javascript.
Дайте вашему якорю класс:
<a class="anchor" id="top"></a>
Затем вы можете установить привязку на смещение выше или ниже, чем там, где оно действительно отображается на странице, сделав его блочным элементом и относительно позиционируя его. -250px расположит якорь 250px
a.anchor {
display: block;
position: relative;
top: -250px;
visibility: hidden;
}
Ответ 2
Я нашел это решение:
<a name="myanchor">
<h1 style="padding-top: 40px; margin-top: -40px;">My anchor</h1>
</a>
Это не создает никакого пробела в содержании, а ссылки привязки действительно приятны.
Ответ 3
Поскольку это является проблемой презентации, чистое решение CSS было бы идеальным. Однако этот вопрос был поставлен в 2012 году, и хотя были предложены относительные решения для позиционирования/отрицательной маржи, эти подходы кажутся довольно хакерскими, создают потенциальные проблемы с потоком и не могут динамически реагировать на изменения в DOM/viewport.
С учетом этого я считаю, что использование JavaScript по-прежнему (февраль 2017 года) - лучший подход. Ниже представлено решение Vanilla-JS, которое будет реагировать как на якорные щелчки, так и на разрешение хеша страницы при загрузке (см. JSFiddle). Измените метод .getFixedOffset()
, если требуются динамические вычисления. Если вы используете jQuery, здесь измененное решение с лучшим делением событий и плавной прокруткой.
(function(document, history, location) {
var HISTORY_SUPPORT = !!(history && history.pushState);
var anchorScrolls = {
ANCHOR_REGEX: /^#[^ ]+$/,
OFFSET_HEIGHT_PX: 50,
/**
* Establish events, and fix initial scroll position if a hash is provided.
*/
init: function() {
this.scrollToCurrent();
window.addEventListener('hashchange', this.scrollToCurrent.bind(this));
document.body.addEventListener('click', this.delegateAnchors.bind(this));
},
/**
* Return the offset amount to deduct from the normal scroll position.
* Modify as appropriate to allow for dynamic calculations
*/
getFixedOffset: function() {
return this.OFFSET_HEIGHT_PX;
},
/**
* If the provided href is an anchor which resolves to an element on the
* page, scroll to it.
* @param {String} href
* @return {Boolean} - Was the href an anchor.
*/
scrollIfAnchor: function(href, pushToHistory) {
var match, rect, anchorOffset;
if(!this.ANCHOR_REGEX.test(href)) {
return false;
}
match = document.getElementById(href.slice(1));
if(match) {
rect = match.getBoundingClientRect();
anchorOffset = window.pageYOffset + rect.top - this.getFixedOffset();
window.scrollTo(window.pageXOffset, anchorOffset);
// Add the state to history as-per normal anchor links
if(HISTORY_SUPPORT && pushToHistory) {
history.pushState({}, document.title, location.pathname + href);
}
}
return !!match;
},
/**
* Attempt to scroll to the current location hash.
*/
scrollToCurrent: function() {
this.scrollIfAnchor(window.location.hash);
},
/**
* If the click event target was an anchor, fix the scroll position.
*/
delegateAnchors: function(e) {
var elem = e.target;
if(
elem.nodeName === 'A' &&
this.scrollIfAnchor(elem.getAttribute('href'), true)
) {
e.preventDefault();
}
}
};
window.addEventListener(
'DOMContentLoaded', anchorScrolls.init.bind(anchorScrolls)
);
})(window.document, window.history, window.location);
Ответ 4
Я тоже искал решение этой проблемы. В моем случае это было довольно легко.
У меня есть список меню со всеми ссылками:
<ul>
<li><a href="#one">one</a></li>
<li><a href="#two">two</a></li>
<li><a href="#three">three</a></li>
<li><a href="#four">four</a></li>
</ul>
И ниже этого заголовка, куда он должен идти.
<h3>one</h3>
<p>text here</p>
<h3>two</h3>
<p>text here</p>
<h3>three</h3>
<p>text here</p>
<h3>four</h3>
<p>text here</p>
Теперь, поскольку у меня есть фиксированное меню в верхней части моей страницы, я не могу просто сделать так, чтобы оно переходило к моему тегу, потому что это будет за меню.
Вместо этого я поместил тег span внутри моего тега с правильным идентификатором.
<h3><span id="one"></span>one</h3>
Теперь используйте 2 строки CSS, чтобы правильно расположить их.
h3{ position:relative; }
h3 span{ position:absolute; top:-200px;}
Измените верхнее значение, чтобы оно соответствовало высоте вашего фиксированного заголовка (или более). Теперь я предполагаю, что это будет работать и с другими элементами.
Ответ 5
FWIW это сработало для меня:
[id]::before {
content: '';
display: block;
height: 75px;
margin-top: -75px;
visibility: hidden;
}
Ответ 6
Чистое решение css, вдохновленное Александром Савиным:
a[name] {
padding-top: 40px;
margin-top: -40px;
display: inline-block; /* required for webkit browsers */
}
При желании вы можете добавить следующее, если цель все еще отключена:
vertical-align: top;
Ответ 7
Это занимает много элементов из предыдущих ответов и объединяется в крошечную (194 байта) анонимную функцию jQuery. Отрегулируйте fixedElementHeight для высоты вашего меню или блокирующего элемента.
(function($, window) {
var adjustAnchor = function() {
var $anchor = $(':target'),
fixedElementHeight = 100;
if ($anchor.length > 0) {
$('html, body')
.stop()
.animate({
scrollTop: $anchor.offset().top - fixedElementHeight
}, 200);
}
};
$(window).on('hashchange load', function() {
adjustAnchor();
});
})(jQuery, window);
Если вам не нравится анимация, замените
$('html, body')
.stop()
.animate({
scrollTop: $anchor.offset().top - fixedElementHeight
}, 200);
с:
window.scrollTo(0, $anchor.offset().top - fixedElementHeight);
Углифицированная версия:
!function(o,n){var t=function(){var n=o(":target"),t=100;n.length>0&&o("html, body").stop().animate({scrollTop:n.offset().top-t},200)};o(n).on("hashchange load",function(){t()})}(jQuery,window);
Ответ 8
Мое решение сочетает в себе цели и до селекторов для нашей CMS. Другие методы не учитывают текст в привязке. Отрегулируйте высоту и отрицательное поле до необходимого смещения...
:target::before {
content: '';
display: block;
height: 180px;
margin-top: -180px;
}
Ответ 9
Я столкнулся с подобной проблемой, к сожалению, после реализации всех вышеперечисленных решений я пришел к следующему выводу.
- У моих внутренних элементов была хрупкая структура CSS и реализация относительного/абсолютного воспроизведения позиции, полностью нарушала дизайн страницы.
- CSS не мой сильный костюм.
Я написал эту простую прокрутку js, которая учитывает смещение, вызванное заголовком, и переместило div примерно на 125 пикселей ниже. Используйте его, как вы сочтете нужным.
HTML
<div id="#anchor"></div> <!-- #anchor here is the anchor tag which is on your URL -->
JavaScript
$(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 - 125 //offsets for fixed header
}, 1000);
return false;
}
}
});
//Executed on page load with URL containing an anchor tag.
if($(location.href.split("#")[1])) {
var target = $('#'+location.href.split("#")[1]);
if (target.length) {
$('html,body').animate({
scrollTop: target.offset().top - 125 //offset height of header here too.
}, 1000);
return false;
}
}
});
Смотрите живую реализацию здесь.
Ответ 10
Для современных браузеров просто добавьте селектор CSS3: target на страницу. Это применимо ко всем якорям автоматически.
:target {
display: block;
position: relative;
top: -100px;
visibility: hidden;
}
Ответ 11
Вы можете сделать это без JS и без изменения HTML. Это только для CSS.
a[id]::before {
content: '';
display: block;
height: 50px;
margin: -30px 0 0;
}
Это добавит псевдоэлемент перед каждым a-тегом с идентификатором. Настройте значения в соответствии с высотой вашего заголовка.
Ответ 12
Как указывает @moeffju, это может быть достигнуто с помощью CSS. Проблема, с которой я столкнулся (что, я удивляюсь, я не видел), - это трюк перекрытия предыдущих элементов с дополнением или прозрачной рамкой, предотвращающий наведение и щелчок действий в нижней части этих разделов, потому что следующее происходит выше в г-го порядка.
Лучшее исправление, которое я нашел, - разместить содержимое раздела в div
, который находится в z-index: 1
:
// Apply to elements that serve as anchors
.offset-anchor {
border-top: 75px solid transparent;
margin: -75px 0 0;
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
}
// Because offset-anchor causes sections to overlap the bottom of previous ones,
// we need to put content higher so links aren't blocked by the transparent border.
.container {
position: relative;
z-index: 1;
}
Ответ 13
Решения с изменяющимся положением не всегда возможны (это может разрушить макет), поэтому я предлагаю следующее:
HTML:
<a id="top">Anchor</a>
CSS
#top {
margin-top: -250px;
padding-top: 250px;
}
Используйте это:
<a id="top"> </a>
чтобы свести к минимуму перекрытие и установить размер шрифта в 1px. Пустой анкер не работает в некоторых браузерах.
Ответ 14
Для той же самой проблемы я использовал простое решение: поместите верхнюю часть 40px на каждый якорь.
Ответ 15
Заимствуя часть кода из ответа, полученного по этой ссылке (автор не указан), вы можете включить хороший эффект сглаживания на якорь, в то время как заставляя его останавливаться на -60px выше якоря, хорошо помещая под фиксированной навигационной панелью бутстрапа (требуется jQuery):
$(".dropdown-menu a[href^='#']").on('click', function(e) {
// prevent default anchor click behavior
e.preventDefault();
// animate
$('html, body').animate({
scrollTop: $(this.hash).offset().top - 60
}, 300, function(){
});
});
Ответ 16
Я столкнулся с этой проблемой и в итоге обработал события кликов вручную, например:
$('#mynav a').click(() ->
$('html, body').animate({
scrollTop: $($(this).attr('href')).offset().top - 40
}, 200
return false
)
Прокрутка анимации необязательна, конечно.
Ответ 17
Вышеуказанные методы не очень хорошо работают, если ваш якорь является элементом таблицы или внутри таблицы (строки или ячейки).
Мне пришлось использовать javascript и привязать к событию окна hashchange
, чтобы обойти это (demo):
function moveUnderNav() {
var $el, h = window.location.hash;
if (h) {
$el = $(h);
if ($el.length && $el.closest('table').length) {
$('body').scrollTop( $el.closest('table, tr').position().top - 26 );
}
}
}
$(window)
.load(function () {
moveUnderNav();
})
.on('hashchange', function () {
moveUnderNav();
});
* Примечание. Событие hashchange недоступно во всех браузерах.
Ответ 18
Это было вдохновлено ответом Шувика - тем же понятием, что и его, только размер фиксированного заголовка не жестко закодирован. Пока ваш фиксированный заголовок находится в первом заголовке node, это должно "просто работать"
/*jslint browser: true, plusplus: true, regexp: true */
function anchorScroll(fragment) {
"use strict";
var amount, ttarget;
amount = $('header').height();
ttarget = $('#' + fragment);
$('html,body').animate({ scrollTop: ttarget.offset().top - amount }, 250);
return false;
}
function outsideToHash() {
"use strict";
var fragment;
if (window.location.hash) {
fragment = window.location.hash.substring(1);
anchorScroll(fragment);
}
}
function insideToHash(nnode) {
"use strict";
var fragment;
fragment = $(nnode).attr('href').substring(1);
anchorScroll(fragment);
}
$(document).ready(function () {
"use strict";
$("a[href^='#']").bind('click', function () {insideToHash(this); });
outsideToHash();
});
Ответ 19
Вместо того, чтобы иметь навигационную панель с фиксированной позицией, которая перекрывается остальной частью страницы (с прокручиваемым всем телом страницы), рассмотрите вместо этого наличие незапропускаемого тела со статическим навигатором и затем наличие страницы содержимое в прокручиваемом div без ограничений.
То есть, у вас такой HTML...
<div class="static-navbar">NAVBAR</div>
<div class="scrollable-content">
<p>Bla bla bla</p>
<p>Yadda yadda yadda</p>
<p>Mary had a little lamb</p>
<h2 id="stuff-i-want-to-link-to">Stuff</h2>
<p>More nonsense</p>
</div>
... и CSS:
.static-navbar {
height: 100px;
}
.scrollable-content {
position: absolute;
top: 100px;
bottom: 0;
overflow-y: scroll;
width: 100%;
}
Это дает желаемый результат в прямом, небедовом способе. Единственная разница в поведении между этим и некоторыми умными хаками CSS, предложенными выше, заключается в том, что полоса прокрутки (в браузерах, которые ее отображают) будет привязана к содержимому div, а не по всей высоте страницы. Вы можете или не можете считать это желательным.
Ответ 20
Я столкнулся с этой проблемой на веб-сайте TYPO3, где все "Content Elements" обернуты чем-то вроде:
<div id="c1234" class="contentElement">...</div>
и я изменил рендеринг, чтобы он выглядел следующим образом:
<div id="c1234" class="anchor"></div>
<div class="contentElement">...</div>
И этот CSS:
.anchor{
position: relative;
top: -50px;
}
Фиксированная верхняя панель высотой 40px, теперь анкеры снова работают и запускают 10px под верхней панелью.
Единственный недостаток этой техники - вы больше не можете использовать :target
.
Ответ 21
Вы можете достичь этого без ID с помощью селектора a[name]:not([href])
css. Это просто ищет ссылки с именем и без href, например. <a name="anc1"></a>
Примерное правило может быть:
a[name]:not([href]){
display: block;
position: relative;
top: -100px;
visibility: hidden;
}
Ответ 22
Добавляя к ответу Циава (спасибо Александру Савину), мне нужно использовать старую школу <a name="...">...</a>
, поскольку мы используем <div id="...">...</div>
для другой цели в нашем коде. У меня были некоторые проблемы с отображением, используя display: inline-block
- первая строка каждого элемента <p>
оказалась слегка правой (в браузерах Webkit и Firefox). Я закончил тем, что пытался использовать другие значения display
, а display: table-caption
отлично работает для меня.
.anchor {
padding-top: 60px;
margin-top: -60px;
display: table-caption;
}
Ответ 23
Я добавил элемент 40px-height .vspace
, удерживающий якорь перед каждым из моих элементов h1
.
<div class="vspace" id="gherkin"></div>
<div class="page-header">
<h1>Gherkin</h1>
</div>
В CSS:
.vspace { height: 40px;}
Он отлично работает, и пространство не забивается.
Ответ 24
как насчет скрытых тегов span со связанными идентификаторами, которые обеспечивают высоту навигационной панели:
#head1 {
padding-top: 60px;
height: 0px;
visibility: hidden;
}
<span class="head1">somecontent</span>
<h5 id="headline1">This Headline is not obscured</h5>
heres the скрипка: http://jsfiddle.net/N6f2f/7
Ответ 25
Вы также можете добавить якорь с последующим attr:
(text-indent:-99999px;)
visibility: hidden;
position:absolute;
top:-80px;
и предоставить родительскому контейнеру относительную позицию.
Прекрасно подходит для меня.
Ответ 26
Еще один поворот к отличному ответу от @Jan заключается в том, чтобы включить его в фиксированный заголовок #uberbar, в котором используется jQuery (или MooTools). (http://davidwalsh.name/persistent-header-opacity)
Я изменил код, поэтому верхняя часть содержимого всегда находится ниже не под фиксированным заголовком, а также добавила привязки из @Jan снова, убедившись, что привязки всегда расположены ниже фиксированного заголовка.
CSS:
#uberbar {
border-bottom:1px solid #0000cc;
position:fixed;
top:0;
left:0;
z-index:2000;
width:100%;
}
a.anchor {
display: block;
position: relative;
visibility: hidden;
}
jQuery (включая настройки как для #uberbar, так и для привязки:
<script type="text/javascript">
$(document).ready(function() {
(function() {
//settings
var fadeSpeed = 200, fadeTo = 0.85, topDistance = 30;
var topbarME = function() { $('#uberbar').fadeTo(fadeSpeed,1); }, topbarML = function() { $('#uberbar').fadeTo(fadeSpeed,fadeTo); };
var inside = false;
//do
$(window).scroll(function() {
position = $(window).scrollTop();
if(position > topDistance && !inside) {
//add events
topbarML();
$('#uberbar').bind('mouseenter',topbarME);
$('#uberbar').bind('mouseleave',topbarML);
inside = true;
}
else if (position < topDistance){
topbarME();
$('#uberbar').unbind('mouseenter',topbarME);
$('#uberbar').unbind('mouseleave',topbarML);
inside = false;
}
});
$('#content').css({'margin-top': $('#uberbar').outerHeight(true)});
$('a.anchor').css({'top': - $('#uberbar').outerHeight(true)});
})();
});
</script>
И, наконец, HTML:
<div id="uberbar">
<!--CONTENT OF FIXED HEADER-->
</div>
....
<div id="content">
<!--MAIN CONTENT-->
....
<a class="anchor" id="anchor1"></a>
....
<a class="anchor" id="anchor2"></a>
....
</div>
Может быть, это полезно для тех, кто любит затухающий заголовок #uberbar fading!
Ответ 27
Решение @AlexanderSavin отлично работает в браузерах WebKit
для меня.
Кроме того, мне пришлось использовать : target псевдокласс, который применяет стиль к выбранному якорю для настройки дополнения в FF
, Opera
и IE9
:
a:target {
padding-top: 40px
}
Обратите внимание, что этот стиль не для Chrome
/Safari
, поэтому вам, вероятно, придется использовать css-хаки, условные комментарии и т.д.
Также я хотел бы заметить, что решение Alexander работает из-за того, что целевой элемент inline
. Если вам не нужна ссылка, вы можете просто изменить свойство display
:
<div id="myanchor" style="display: inline">
<h1 style="padding-top: 40px; margin-top: -40px;">My anchor</h1>
</div>
Ответ 28
Здесь решение, которое мы используем на нашем сайте. Отрегулируйте переменную headerHeight
до любой высоты вашего заголовка. Добавьте класс js-scroll
к якорю, который должен прокручиваться по щелчку.
// SCROLL ON CLICK
// --------------------------------------------------------------------------
$('.js-scroll').click(function(){
var headerHeight = 60;
$('html, body').animate({
scrollTop: $( $.attr(this, 'href') ).offset().top - headerHeight
}, 500);
return false;
});