Javascript-форум (https://javascript.ru/forum/)
-   jQuery (https://javascript.ru/forum/jquery/)
-   -   Некорректно работает самописный плагин (https://javascript.ru/forum/jquery/50626-nekorrektno-rabotaet-samopisnyjj-plagin.html)

ILL-JAH 04.10.2014 21:26

Некорректно работает самописный плагин
 
Всем привет. Заморочился я тут запилить собственный плагин скролла до якоря. Вот один экземпляр скипта на странице работает норм, а два уже некорректно. Не могу понять в чём дело. Окинте, пожалуйста, незамыленным взглядом мой быдлокод и может вы увидите что я проглядел по причине своих скромных знаний js.

<head>
  <meta charset="utf-8" />
  <title>$(selector).waypoint(offset)</title>
  <style type="text/css">
    html, body, div, ul, li, a, h1 {margin: 0; padding: 0}
    a:active, a:focus {outline: 0}
    ul {list-style: none}
    a, a:visited {color: black}
    a:hover, a.active {background: gray}
    a:hover, a:active {text-decoration: none}

    .navBox {position: fixed; width: 100%; top: 0; background: lightgray}
    .nav {text-align: center}
    .nav .item {display: inline-block; *display: inline; *zoom: 1}
    .nav .ref, .sidebar .ref {display: block; text-decoration: none; font: normal 20px/50px Arial; padding: 0 20px; text-transform: uppercase}

    .main {margin-top: 50px}
    .section {height: 1000px; border: 1px solid black; margin-top: 20px}
    .subSection {height: 500px; margin-top: 461px; border: 1px solid green}

    .sidebar {position: fixed; right: 0; background: lightgray}
  </style>
  <script type="text/javascript" src="jquery-1.10.2.min.js"></script>
  <script type="text/javascript">
    /**
    * Вариант метода waypoint(offset), который перелинковывает все ссылки селектора с якорями.
    * $(selector).waypoint(offset);
    * Примеры использования.
    * $(selector).waypoint('25%');
    * $(selector).waypoint('300px');
    */
    (function ($)
    {
      //Декларация метода waypoint.
      $.fn.waypoint = function (offset)
      {
        var refs = this;
        this.elems = $('*[id]');
        //Помещаем с свойство linx все ссылки, указывающие на элемент.
        for (var a = 0; a < this.elems.length; a++)
        {
          var cur = this.elems[a].id;
          if (!this.elems[a].linx || this.elems[a].linx.length == 0)
            this.elems[a].linx = this.filter(function (index) { return this.hash == '#' + cur; });
          for (var b = 0; b < this.elems[a].linx.length; b++)
          {
            this.elems[a].linx.eq(b)[0].anchor = this.elems[a];
          }
        }

        //Вешаем анимацию скролла при клике по ссылке.
        this.each(function ()
        {
          $(this).on('click', function (event)
          {
            event.preventDefault();
            var anchor = this.anchor;
            $('html, body').animate({ scrollTop: anchor.offsetTop }, 'fast');
          });
        });

        //Если объект offset не передан, устанавливаем значение по умолчанию,
        //равное половине высоты окна браузера.
        if (!offset) var offset = parseInt($(window).height() / 2, 10);
        else
        {
          //Проценты.
          if (offset.search('%') != -1)
            offset = parseInt($(window).height() / 100 * parseInt(offset, 10), 10);

          //Пиксели.
          else if (offset.search('px') != -1)
            offset = parseInt(offset, 10);
        }

        //Обработчик события скролла.
        function waypoint(event)
        {
          //console.log(waypoint.params);
          var switchPoint = waypoint.params;
          waypoint.elems.each(function ()
          {
            if (this.offsetTop < $(window).scrollTop() + switchPoint &&
              this.offsetTop + $(this).height() > $(window).scrollTop() + switchPoint)
            {
              waypoint.elems.each(function () { this.linx.removeClass('active'); /*console.log(this.id + ' ' + this.linx[0]);*/ });
              this.linx.addClass('active');
              console.log(waypoint.params);
            }
          });
        }
        waypoint.params = offset;
        waypoint.elems = this.elems;

        if (!window.scrollHandlers) window.scrollHandlers = [];
        window.scrollHandlers.push(waypoint);

        if (!window.runScrollHandlers)
        {
          window.runScrollHandlers = function ()
          {
            for (var a = 0; a < window.scrollHandlers.length; a++)
              window.scrollHandlers[a]();
          }
          $(window).bind('scroll', window.runScrollHandlers);
        }
        window.runScrollHandlers();

        return this;
      }
    })(jQuery);

    //Использование метода waypoint.
    $(function ()
    {
      $('.nav .ref').waypoint('0px');
      $('.sidebar .ref').waypoint('300px');
    });
  </script>
</head>
<body>
  <div class="navBox">
    <ul class="nav">
      <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#block1">block1</a></li>
      <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#block2">block2</a></li>
      <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#block3">block3</a></li>
    </ul>
  </div>

  <ul class="sidebar">
    <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#Div1">Div1</a></li>
    <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#Div2">Div2</a></li>
    <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#Div3">Div3</a></li>
  </ul>

  <div class="main">
    <div class="section" id="block1">
      <h1>block1</h1>
      <div class="subSection" id="Div1">
        <h1>Div1</h1>
      </div>
    </div>
    <div class="section" id="block2">
      <h1>block2</h1>
      <div class="subSection" id="Div2">
        <h1>Div2</h1>
      </div>
    </div>
    <div class="section" id="block3">
      <h1>block3</h1>
      <div class="subSection" id="Div3">
        <h1>Div3</h1>
      </div>
    </div>
  </div>
</body>

bes 05.10.2014 10:08

<style type="text/css">
    html, body, div, ul, li, a, h1 {margin: 0; padding: 0}
    a:active, a:focus {outline: 0}
    ul {list-style: none}
    a, a:visited {color: black}
    a:hover, a.active {background: gray}
    a:hover, a:active {text-decoration: none}

    .navBox {position: fixed; width: 100%; top: 0; background: lightgray}
    .nav {text-align: center}
    .nav .item {display: inline-block; *display: inline; *zoom: 1}
    .nav .ref, .sidebar .ref {display: block; text-decoration: none; font: normal 20px/50px Arial; padding: 0 20px; text-transform: uppercase}

    .main {margin-top: 50px}
    .section {height: 1000px; border: 1px solid black; margin-top: 20px}
    .subSection {height: 500px; margin-top: 461px; border: 1px solid green}

    .sidebar {position: fixed; right: 0; background: lightgray}
	.section {
		padding-top: 60px;
	}
  </style>

<body>
  <div class="navBox">
    <ul class="nav">
      <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#block1">block1</a></li>
      <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#block2">block2</a></li>
      <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#block3">block3</a></li>
    </ul>
  </div>

  <ul class="sidebar">
    <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#Div1">Div1</a></li>
    <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#Div2">Div2</a></li>
    <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#Div3">Div3</a></li>
  </ul>

  <div class="main">
    <div class="section" id="block1">
      <h1>block1</h1>
      <div class="subSection" id="Div1">
        <h1>Div1</h1>
      </div>
    </div>
    <div class="section" id="block2">
      <h1>block2</h1>
      <div class="subSection" id="Div2">
        <h1>Div2</h1>
      </div>
    </div>
    <div class="section" id="block3">
      <h1>block3</h1>
      <div class="subSection" id="Div3">
        <h1>Div3</h1>
      </div>
    </div>
  </div>
</body>

<script>
document.addEventListener("click", function (e) {
	var target = e.target;
	if (target.className == "ref") {
		e.preventDefault();
		var index = Array.prototype.indexOf.call(document.getElementsByClassName("ref"), target);
		document.querySelectorAll(".section")[index].scrollIntoView();
	}
});
</script>

рони 05.10.2014 10:55

bes,
у меня что-то клики по Div не срабатывают ... и возможно кроме отмены действия по умолчанию, требуется отмена всплытия.

bes 05.10.2014 11:52

Цитата:

Сообщение от рони
bes,
у меня что-то клики по Div не срабатывают ... и возможно кроме отмены действия по умолчанию, требуется отмена всплытия.

ну это просто демонстрация, что есть такой метод scrollIntoView, делалось только для block (только сейчас заметил, что у div тоже <a class="ref" :) )
да, всплытие тоже надо отменить, вешать не на документ и т.п.

ILL-JAH 05.10.2014 13:42

Пасиб, парни. Подкинули "пищи для размышления".

ILL-JAH 18.10.2014 00:44

Подправил свой плагин. Теперь работает почти так, как нужно. Вот только когда координата срабатывания обработчика (offset) оказывается в промежутке между блоками, класс active имеют два элемента section. Хотя по идее должно быть наоборот: ни один элемент div.section в этот момент не должен иметь класс active. Честное слово даже не знаю куда копать. По умолчанию координата offset - середина окна браузера. Зелёный прямоугольник с правой стороны (div.offset1) в тестовых целях как раз имеет высоту 50%. Опять что-то проглядел что ли?

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta charset="utf-8" />
  <title></title>
  <style type="text/css">
    html, body, div, ul, li, a, h1 {margin: 0; padding: 0}
    a:active, a:focus {outline: 0}
    ul {list-style: none}
    a, a:visited {color: black}
    a:hover, a.active {background: gray}
    a:hover, a:active {text-decoration: none}

    .navBox, .bottomNavBox {position: fixed; width: 100%; background: lightgray}
    .navBox {top: 0}
    .bottomNavBox {bottom: 0}
    .nav {text-align: center}
    .nav .item {display: inline-block; *display: inline; *zoom: 1}
    .nav .ref, .sidebar .ref {display: block; text-decoration: none;
      font: normal 20px/50px Arial; padding: 0 20px; text-transform: uppercase}

    .main {margin-top: 50px}
    .section {height: 1000px; border: 1px solid black; margin-bottom: 50px}
    .subSection {height: 500px; margin-top: 461px; border: 1px solid green}

    .sidebar {position: fixed; right: 0; background: lightgray}
	  .section {padding-top: 60px}

    .offset1 {position: fixed; top: 0; width: 10px; height: 50%; background:
    green; z-index: 2}
  </style>
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
  <script type="text/javascript">
    /**
     * Вариант метода waypoint(offsetTop), который перелинковывает все ссылки
     * селектора с якорями.
     * $(selector).waypoint(offsetTop);
     * Примеры использования.
     * $(selector).waypoint('25%');
     * $(selector).waypoint('300px');
     */
    (function ($)
    {
      //Декларация метода waypoint.
      $.fn.waypoint = function (offsetTop)
      {
        var refs = this;
        var elems = $('*[id]');
        for(var a = 0; a < refs.length; a++)
        {
          var hash = refs[a].hash.substring(1);
          refs[a].anchor = elems.find('*[id=' + hash + ']');
          elems.filter('[id=' + hash + ']').each(function()
          {
            if(!this.refs) this.refs = refs.filter(
              function(index){ return this.hash == '#' + hash; });
          });
        }

        //Если объект offsetTop не передан, устанавливаем значение по умолчанию,
        //равное половине высоты окна браузера.
        if (!offsetTop) var offsetTop = parseInt($(window).height() / 2, 10);
        else
        {
          //Проценты.
          if (offsetTop.search('%') != -1)
            offsetTop = parseInt($(window).height() / 100 * parseInt(offsetTop, 10), 10);

          //Пиксели.
          else if (offsetTop.search('px') != -1)
            offsetTop = parseInt(offsetTop, 10);
        }

        //Клик по ссылке инициирует скролл до якорного элемента.
        $('body').on('click', function(event)
        {
          var eventElem = event.target || event.srcElement;
          if($(refs).index(eventElem) != -1)
          {
            event.preventDefault();
            refs.removeClass('active');
            refs.filter(function (index)
              { return this.hash == eventElem.hash; }).addClass('active');
            $('html, body').animate({scrollTop:
              $(eventElem.hash).offset().top}, 'fast');
          }
        });

        //Функция присвоения класса active ссылкам на основе данных о положении
        //метки offsetTop относительно якорного элемента.
        function waypoint(event)
        {
          event.data.elems.each(function()
          {
            //Если мета offsetTop находится внутри якорного элемента, то
            //добавляем класс active ссылкам с хэшем на этот элемент.
            if (this.offsetTop < $(window).scrollTop() + event.data.offsetTop &&
              this.offsetTop + $(this).innerHeight() > $(window).scrollTop() + event.data.offsetTop)
            {
              event.data.refs.removeClass('active');
              this.refs.addClass('active');
            }
          });
        }

        var data = {
          offsetTop: offsetTop,
          refs: $(refs),
          elems: elems
        };

        $(window).on('scroll', data, waypoint);

        //Инициализация метода.
        waypoint({data: data});

        return this;
      }
    })(jQuery);

    //Использование метода waypoint.
    $(function ()
    {
      $('.nav .ref').waypoint();
      $('.sidebar .ref').waypoint();
    });
  </script>
</head>
<body>
  <div class="offset1"></div>
  <div class="navBox">
    <ul class="nav">
      <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#block1">block1</a></li>
      <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#block2">block2</a></li>
      <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#block3">block3</a></li>
    </ul>
  </div>

  <ul class="sidebar">
    <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#Div1">Div1</a></li>
    <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#Div2">Div2</a></li>
    <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#Div3">Div3</a></li>
  </ul>

  <div class="main">
    <div class="section" id="block1">
      <h1>block1</h1>
      <div class="subSection" id="Div1">
        <h1>Div1</h1>
      </div>
    </div>
    <div class="section" id="block2">
      <h1>block2</h1>
      <div class="subSection" id="Div2">
        <h1>Div2</h1>
      </div>
    </div>
    <div class="section" id="block3">
      <h1>block3</h1>
      <div class="subSection" id="Div3">
        <h1>Div3</h1>
      </div>
    </div>
  </div>

  <div class="bottomNavBox">
    <ul class="nav">
      <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#block1">block1</a></li>
      <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#block2">block2</a></li>
      <li class="item"><a class="ref" href="$(selector).waypoint(offset).html#block3">block3</a></li>
    </ul>
  </div>
</body>
</html>

bes 18.10.2014 08:53

где [html run и подключённая jquery

ILL-JAH 18.10.2014 16:13

Цитата:

Сообщение от bes (Сообщение 336317)
где [html run и подключённая jquery

Подключил.

bes 18.10.2014 16:52

Цитата:

Сообщение от ILL-JAH
Подключил.

надо сделать ещё [html run height=800 hide]

Цитата:

Сообщение от ILL-JAH
Вот только когда координата срабатывания обработчика (offset) оказывается в промежутке между блоками, класс active имеют два элемента section. Хотя по идее должно быть наоборот: ни один элемент div.section в этот момент не должен иметь класс active. Честное слово даже не знаю куда копать. По умолчанию координата offset - середина окна браузера. Зелёный прямоугольник с правой стороны (div.offset1) в тестовых целях как раз имеет высоту 50%. Опять что-то проглядел что ли?

не понял о чём ты и как воспроизвести ситуацию

ILL-JAH 18.10.2014 17:02

Здесь в маленьком окошке ничего не понять. Нужно в отдельном html-файле всё сохранить и запустить. Тогда всё станет понятно. Когда медленно прокручиваешь и середина экрана попадает между блоками div.section, то 2 элемента меню загараются серым. А по-хорошему не должен гореть ни один.


Часовой пояс GMT +3, время: 16:40.