Как получить динамические значения смещения данных для метода аффикса Bootstrap 3

Я хотел бы использовать метод Affix, описанный в документации Bootstrap (http://getbootstrap.com/javascript/#affix), однако панель навигации, которую я хотел бы зафиксировать в верхней части страницы после ее прокрутки, может иметь разные значения смещения в зависимости от содержимого над ней.

Вот пример панели навигации:

<div class="navbar navbar-default" data-spy="affix" data-offset-top="200">
  <ul class="nav navbar-nav">
    <li class="active"><a href="#">Link</a></li>
    <li><a href="#">Link 1</a></li>
    <li><a href="#">Link 2</a></li>
  </ul>
</div>

Как вы можете видеть, data-offset-top в настоящее время установлено на 200. Это прекрасно работает, если содержимое выше имеет высоту 200 пикселей, но содержимое выше является динамическим, и поэтому высота над этой панелью навигации не всегда одинакова. Как я могу сделать vale для data-offset-top динамическим?

Я предполагаю, что мне придется использовать способ javascript, но я не уверен.


person bigmike7801    schedule 09.09.2013    source источник
comment
Мне нужен был способ пересчитать значение при изменении размера страницы, а также добавить держатель пробела, чтобы прикрепление выполнялось гладко, например. нет перескакивания страниц, когда содержимое прикрепляется. Решение - › stackoverflow.com/a/47429990/996010   -  person Hrvoje Golcic    schedule 22.11.2017


Ответы (7)


Вы можете использовать jQuery, чтобы получить высоту динамического содержимого выше navbar. Например:

$('#nav').affix({
      offset: {
        top: $('header').height()
      }
}); 

Рабочая демонстрация: http://bootply.com/69848

В некоторых случаях нужно также вычислить offset.bottom, чтобы определить, когда "открепить" элемент. Вот пример affix-bottom

person Zim    schedule 09.09.2013
comment
используйте .outerHeight() вместо .height(), чтобы включить границу и отступы - person Alicia; 07.08.2016

Я был абсолютно разочарован тем, как это работает, каждый раз выполняя какой-то пользовательский расчет и создавая смещение по высоте элементов над желаемым аффиксом.

Вот аффикс в лучшем случае.

Обычный аффикс boostrap не учитывает естественное смещение верхней позиции элементов, вам нужно вручную передать его как атрибут. Это позаботится об этом и учитывает изменение верхнего смещения при изменении размера окна.

var $attribute = $('[data-smart-affix]');
$attribute.each(function(){
  $(this).affix({
    offset: {
      top: $(this).offset().top,
    }
  })
})
$(window).on("resize", function(){
  $attribute.each(function(){
    $(this).data('bs.affix').options.offset.top = $(this).offset().top
  })
})

Я также опубликовал это в обзоре кода.

Не забудьте добавить атрибут data-smart-affix к элементу аффикса.

Попробуйте этот пример кода: https://jsfiddle.net/NabiKAZ/qyrauogw/
( В этом примере высота красного раздела может составлять 100 пикселей, 200 пикселей или 300 пикселей в зависимости от ширины окна, поэтому при прокрутке необходимо различать data-offset-top.)

person ThomasReggi    schedule 19.03.2015
comment
ты мой герой! Спасибо, чувак .. PS для других: при использовании этого кода не забудьте добавить атрибут data-smart-affix к вашему элементу аффикса. ;) - person Mike; 12.08.2015
comment
Лучший ответ! хорошо сделано. это помогло мне решить мою проблему - person Bogdan Le; 14.10.2015
comment
В вашем коде есть ошибка, используйте $(this).data('bs.affix').options.offset.top вместо $(this).data('bs.affix').options.offset. Также могу предложить использовать [data-offset-top-dynamic вместо [data-smart-affix] - person Hrvoje Golcic; 15.11.2017

Data-smart-affix ThomasReggi не работал у меня в некоторых крайних случаях (при изменении размера документа или когда прикрепленный столбец был слишком высоким). Но это дало мне простую идею: добавить пустой элемент перед прикрепленным содержимым и использовать его для получения смещения. Хорошо работает при изменении размера.

Пока я занимался этим, я также установил ширину элемента в соответствии с его естественной шириной в исходном родительском элементе. Вот мое решение:

$('[data-affix-after]').each( function() {
  var elem = $(this);
  var parent_panel = elem.parent();
  var prev = $( elem.data('affix-after') );

  if( prev.length != 1 ) {
    /* Create a new element immediately before. */
    prev = elem.before( '<div></div>' ).prev();
  }

  var resizeFn = function() {
      /* Set the width to it's natural width in the parent. */
      var sideBarNavWidth = parent_panel.width()
          - parseInt(elem.css('paddingLeft'))
          - parseInt(elem.css('paddingRight'))
          - parseInt(elem.css('marginLeft'))
          - parseInt(elem.css('marginRight'))
          - parseInt(elem.css('borderLeftWidth'))
          - parseInt(elem.css('borderRightWidth'));
      elem.css('width', sideBarNavWidth);

      elem.affix({
          offset: {
              top: prev.offset().top + prev.outerHeight(true),
              bottom: $('body>footer').outerHeight(true)
          }
      });
  };

  resizeFn();
  $(window).resize(resizeFn);
});

HTML, который идет с ним:

<div id='before-affix'></div><!-- leave this empty -->
<div data-affix-after='#before-affix'>
    Put content to affix here.
</div>

or

<div data-affix-after='#create'><!-- any ID that doesn't exist -->
    Put content to affix here.
</div>

Требуемый CSS:

.affix { top: 0px },
.affix-bottom { position: absolute; }

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

.affix, .affix-bottom { position: static; }

внутри соответствующего медиа-запроса.

GI

person gi1242    schedule 04.09.2015

Я предлагаю вам добавить обертку вокруг прикрепленного div, чтобы избежать «перехода прокрутки» с прикрепленной панелью навигации. Томас опубликовал хорошее решение, но оно не включает свернутое пространство после аффикса и не работает в версии Boostrap 2.x, которую я должен использовать. Поэтому я решил написать полностью новую реализацию аффикса. Для этого не нужен boostrap, просто jquery. Это мой первый джаваскрипт, так что помилуй :) но может кому поможет. Всегда приятно иметь решение без бутстрапа

function myaffix() {

var affixoffset = $('.navbar').offset().top

$('#nav-wrapper').height($(".navbar").height());

$(window).scroll(function () {

    if ($(window).scrollTop() <= affixoffset) {
        $('.navbar').removeClass('affix');
    } else {
        $('.navbar').addClass('affix');
    }
});

};

$(document).ready(function () {

   myaffix();

   $(window).on("resize", function () {
       myaffix();
   });

});

Вы можете найти пример здесь: http://jsfiddle.net/3ss7fk92/

person pixelofficer    schedule 16.04.2015

Скелли выше прав, и нет лучшего способа сделать это. Единственное возможное решение с «родным» бутстрапом — добавить параметры аффикса с вызовом JavaScript (как упоминалось выше в https://stackoverflow.com/a/18702972/2546679).

Особенно невозможно использовать атрибут data-offset для достижения того же эффекта (даже если это было бы намного удобнее, и даже если документация по Affix на http://getbootstrap.com/2.3.2/javascript.html#affix вызывает надежду, что это можно сделать).

Так что можно написать

data-offset='{"top":42}'

но одному не разрешено писать, например:

data-offset='{"top":$("#banner").height()}'

Причина в том, что атрибуты данных анализируются через JQuery .data() API, который позволяет анализировать только чистые значения JSON, см. https://api.jquery.com/data/.

person haui    schedule 14.06.2015
comment
Ваш ответ не динамичен, а просто статичен. - person bigmike7801; 14.06.2015
comment
Да - это не отдельный ответ, а дополнение к ответу Скелли (где мне не разрешено комментировать). Я просто попытался уточнить, что бессмысленно пытаться выразить динамическое вычисление аффикса без использования пользовательского JavaScript на странице — что я пробовал в течение нескольких часов — включая отладку подпрограмм начальной загрузки и JQuery. - person haui; 15.06.2015

Спасибо ThomasReggi за идею. Но может быть правильнее написать:

$(this).data('bs.affix').options.offset.top = $(this).offset().top

для таких, как я, которым тоже нужно нижнее значение, которое будет писать аналогично.

Кстати, я также думаю обернуть его в декоратор на основе setTimeout (что-то вроде debounce).

person discodanser    schedule 20.09.2015

Раньше у меня было много проблем с этим, и по какой-то причине я не мог получить ни одно из решений, предложенных другими людьми. Я относительно новичок в Bootstrap и JQuery, поэтому я, вероятно, делал что-то шаткое, чего не улавливал. Несмотря на это, я нашел решение, которое работает очень хорошо, хотя, строго говоря, оно не использует функциональность аффикса, как предполагает Bootstrap. Я сохранил свой #thisElement.affix {top: desiredFixedPosition;} CSS без изменений, но избавился от атрибутов data-spy и data-offset-top из HTML-тега #thisElement. Затем я жестко закодировал это в JQuery:

var newAffix = function() {
    if($("otherElementsAboveThisElement").height() <= $(window).scrollTop()) {
        $("#thisElement").addClass("affix");
    } else {
        $("#thisElement").removeClass("affix");
    }
}

$(document).ready(function() {
    $(window).resize(newAffix);
    $(window).scroll(newAffix);
}

Насколько я проверял, это работает независимо от того, насколько вы изменяете размер страницы или прокручиваете, и если вы проявите творческий подход к тому, как вы вызываете это в функции $(document).ready(), вы даже можете оставить меню в правильном положении во время анимации высоты. Как уже говорилось, технически это не полностью «одобренное Bootstrap» решение, но оно достаточно хорошо интегрируется в сайт Bootstrap, поэтому для некоторых должно работать :)

person Patrick Menard    schedule 18.10.2017