介绍一个神奇的jquery插件——当当当当…jquery.pin.js

它可以把某个你想要的元素定在某个容器里。

听着是不是有点耳熟,貌似使用 position:fixed 固定定位就可以了。但问题在于固定定位往往只能固定在整个文档中,不能固定在任意一个容器里。

使用jquery.pin.js就能轻松办到。

用法也超级简单,跟大多数jquery插件一样,调用一个方法 .pin() 就可以了。

Usage(用法)

“钉”住某个元素

$(".pinned").pin()

将某元素“钉”在容器内

$(".pinned").pin({
containerSelector: ".container" })

在小尺寸的屏幕上禁用Pin效果

$(".pinned").pin({
minWidth: 940 })

简单的示例

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>EXAMPLE</title>
  <link rel="stylesheet" href="css/base.css">
  <link rel="shortcut icon" href="favicon" type="image/x-icon">
</head>
<body>

  <!-- 此处省略一万行代码-->

  <div class="container">
    ......
    ......
    <div class="pinned">......</div>
    ......
  </div>

  <!-- 此处也省略了一万行代码-->

  <!-- 包含jQuery和jquery.pin -->
  <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
  <script src="jquery.pin.js"></script>

  <!-- 把它pin住! -->
  <script>
    $(".pinned").pin({
      containerSelector: ".container", minWidth: 940;
    });
  </script>
</body>
</html>

源码

(function ($) {
    "use strict";
    $.fn.pin = function (options) {
        var scrollY = 0, elements = [], disabled = false, $window = $(window);

        options = options || {};

        var recalculateLimits = function () {
            for (var i=0, len=elements.length; i<len; i++) {
                var $this = elements[i];

                if (options.minWidth && $window.width() <= options.minWidth) {
                    if ($this.parent().is(".pin-wrapper")) { $this.unwrap(); }
                    $this.css({width: "", left: "", top: "", position: ""});
                    if (options.activeClass) { $this.removeClass(options.activeClass); }
                    disabled = true;
                    continue;
                } else {
                    disabled = false;
                }

                var $container = options.containerSelector ? $this.closest(options.containerSelector) : $(document.body);
                var offset = $this.offset();
                var containerOffset = $container.offset();
                var parentOffset = $this.offsetParent().offset();

                if (!$this.parent().is(".pin-wrapper")) {
                    $this.wrap("<div class='pin-wrapper'>");
                }

                var pad = $.extend({
                  top: 0,
                  bottom: 0
                }, options.padding || {});

                $this.data("pin", {
                    pad: pad,
                    from: (options.containerSelector ? containerOffset.top : offset.top) - pad.top,
                    to: containerOffset.top + $container.height() - $this.outerHeight() - pad.bottom,
                    end: containerOffset.top + $container.height(),
                    parentTop: parentOffset.top
                });

                $this.css({width: $this.outerWidth()});
                $this.parent().css("height", $this.outerHeight());
            }
        };

        var onScroll = function () {
            if (disabled) { return; }

            scrollY = $window.scrollTop();

            var elmts = [];
            for (var i=0, len=elements.length; i<len; i++) {          
                var $this = $(elements[i]),
                    data  = $this.data("pin");

                if (!data) { // Removed element
                  continue;
                }

                elmts.push($this); 

                var from = data.from - data.pad.bottom,
                    to = data.to - data.pad.top;

                if (from + $this.outerHeight() > data.end) {
                    $this.css('position', '');
                    continue;
                }

                if (from < scrollY && to > scrollY) {
                    !($this.css("position") == "fixed") && $this.css({
                        left: $this.offset().left,
                        top: data.pad.top
                    }).css("position", "fixed");
                    if (options.activeClass) { $this.addClass(options.activeClass); }
                } else if (scrollY >= to) {
                    $this.css({
                        left: "",
                        top: to - data.parentTop + data.pad.top
                    }).css("position", "absolute");
                    if (options.activeClass) { $this.addClass(options.activeClass); }
                } else {
                    $this.css({position: "", top: "", left: ""});
                    if (options.activeClass) { $this.removeClass(options.activeClass); }
                }
          }
          elements = elmts;
        };

        var update = function () { recalculateLimits(); onScroll(); };

        this.each(function () {
            var $this = $(this), 
                data  = $(this).data('pin') || {};

            if (data && data.update) { return; }
            elements.push($this);
            $("img", this).one("load", recalculateLimits);
            data.update = update;
            $(this).data('pin', data);
        });

        $window.scroll(onScroll);
        $window.resize(function () { recalculateLimits(); });
        recalculateLimits();

        $window.load(update);

        return this;
      };
})(jQuery);
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐