Dynamically add and remove the div on scroll?

2020-06-17 07:02发布

问题:

I tried to add and remove div tags while scrolling like the Dojo grid works. I only want to display 7 div tags.

While scrolling left inside the container, when the first div tag (on the left side) was hidden from the webpage, then that hidden div is removed from the container and a new one is attached onto the right side.

The same process should be applied while scrolling to the right.

It's similar to this example. But in stead of scrolling the <tr> tag, I want to scroll through <div>'s.

This is what I tried before: https://jsfiddle.net/9y2ptsbg/3/ How can I do it? If there's any plugin out there (like Dojo), it's also helpful.

回答1:

maybe this js fiddle would help ?
https://jsfiddle.net/9y2ptsbg/12/

var container = $("#container"),
info = $("#info");

var j = 0;
var colors = ['rgba(143, 146, 199, 0.49)', 'rgba(199, 143, 186, 0.49)', 'rgba(149, 199, 143, 0.49)', 'rgba(229, 86, 61, 0.49)', 'rgba(212, 229, 61, 0.49)', 'rgba(206, 61, 229, 0.49)', 'rgba(229, 157, 61, 0.49)', 'rgba(61, 165, 229, 0.49)', 'rgba(61, 229, 133, 0.49)', 'rgba(229, 61, 61, 0.49)', 'rgba(116, 61, 229, 0.49', 'rgba(218, 229, 61, 0.49)', 'rgba(21, 43, 157, 0.49)', 'rgba(153, 157, 21, 0.49)', 'rgba(199, 143, 186, 0.49)', 'rgba(149, 199, 143, 0.49)', 'rgba(229, 86, 61, 0.49)', 'rgba(212, 229, 61, 0.49)', 'rgba(206, 61, 229, 0.49)', 'rgba(229, 157, 61, 0.49)', 'rgba(61, 165, 229, 0.49)', 'rgba(61, 229, 133, 0.49)', 'rgba(229, 61, 61, 0.49)', 'rgba(116, 61, 229, 0.49', 'rgba(218, 229, 61, 0.49)', 'rgba(21, 43, 157, 0.49)', 'rgba(153, 157, 21, 0.49)', 'rgba(199, 143, 186, 0.49)', 'rgba(149, 199, 143, 0.49)']

var ary = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36'],
cursor = 0,
attachDiv = function (_curr) {
container.empty();
var j = 0;

for (var i = _curr; i < _curr + 8; i++) {

    container.append('<div  class = "blocks blocks' + i + '" style="left:' +   (j * 25) + '%; background:' + colors[i] + ';">' + ary[i] + '</div>');
    j++;
}
};
var hasScrolled = false,
locked = false,
ticker = function () {
    if (hasScrolled && !locked) {
        locked = true;
        var xz = container.scrollLeft(),
            maxScrollLeft = container.get(0).scrollWidth - container.get(0).clientWidth,
            middle = maxScrollLeft / 2;
        if (xz == 0) {
            cursor = Math.max(0, cursor - 4);
        } else if (xz == maxScrollLeft) {
            cursor = Math.min(cursor + 4, ary.length - 8)
        }
        attachDiv(cursor);
        container.scrollLeft(middle);
        info.text(cursor);
        locked = false;
    }
    hasScrolled = false;
}
setInterval(ticker, 250);
    container.on('scroll', function () {
    hasScrolled = true;
});

attachDiv(0);


回答2:

Yes it's possible. First of all, you will need something like this as structure:

<div class="scroll">
    <div class="container">
        <div class="element element-1">1</div>
        <div class="element element-2">2</div>
        <div class="element element-3">3</div>
        <div class="element element-4">4</div>
        <div class="element element-5">5</div>
        <div class="element element-6">6</div>
    </div>
</div>

The .scroll element will be your scrolling container, it's limited in either width or height (depending on how you want to scroll) and has an overflow.

The .container on the other hands takes the highest possible width, so for 6 elements it would be element width * 6.

Finally, because you want to create a left/right scrolling animation, you probably want to set float: left to all .element nodes.

The JavaScript code isn't hard either. You want to add an onScroll event handler to the .scroll element, you can do that like this:

query(".scroll").on("scroll", function(evt) {

});

Then you want to detect whether or not you're scrolling to the left or right. You can do that by comparing the scrolling offset to the previous scrolling offset, for example:

if (lastPos && lastPos - evt.target.scrollLeft > 0) {
    // Scrolling to the left
} else if (lastPos && lastPos - evt.target.scrollLeft < 0) {
    // Scrolling to the right
}
lastPos = evt.target.scrollLeft;

Now, inside the if, you want to loop over all your .element's, detect whether or not on the left- or rightside of the visible portion, and if it is on one of those sides, you move it to the other side (depending on the scrolling direction).

To check whether or not the element is on the left- or right-side of the .scroll, I'm using Element.getBoundingClientRect():

var isLeftOfContainer = function(element) {
    var bounds = element.getBoundingClientRect();
    return (-bounds.left + element.parentNode.offsetLeft) >= bounds.width;
};

var isRightOfContainer = function(element) {
    var bounds = element.getBoundingClientRect();
    var box = element.parentNode.parentNode.getBoundingClientRect();
    return bounds.left - element.parentNode.offsetLeft > box.width;
};

To move the node to the other side, I'm using dojo/dom-construct::place(). You can add either "first" or "last" as third parameter, depending on the direction you're scrolling, for example:

domConstruct.place(elem, elem.parentNode, "first");

This will move the element (elem) to the first position of the parent node. Which is the thing we want to do when we're scrolling to the left.

To loop over all elements you can use dojo/query, for example:

query(".element").forEach(function(element) {

});

Remember that when you're scrolling to the left, you want to loop through the array from the last element to the first element, so that if 2 elements got hidden at the same time, the 6th element is added as first element before adding the 5th element as first element. This ensures that you always add the elements in the right order.

Then finally you have to adjust the scrolling position of the .scroll element. If you move a DOM node to the other side of the list, it has a side effect that all elements will move. This will lead to weird/buggy behavior if you don't move back the scrolling position.

You can do that by adjusting the scrollLeft attribute of the .scroll element.

Everything combined you could come up with something like this: http://jsfiddle.net/c3u6bfmf/



回答3:

Try

$(function() {
          
  // adjust `colors` length to multiples of 7
  var colors = [
    "rgba(143, 146, 199, 0.49)",
    "rgba(199, 143, 186, 0.49)",
    "rgba(149, 199, 143, 0.49)",
    "rgba(229, 86, 61, 0.49)",
    "rgba(212, 229, 61, 0.49)",
    "rgba(206, 61, 229, 0.49)",
    "rgba(229, 157, 61, 0.49)",
    "rgba(61, 165, 229, 0.49)",
    "rgba(61, 229, 133, 0.49)",
    "rgba(229, 61, 61, 0.49)",
    "rgba(116, 61, 229, 0.49",
    "rgba(218, 229, 61, 0.49)",
    "rgba(21, 43, 157, 0.49)",
    "rgba(153, 157, 21, 0.49)",
    "rgba(199, 143, 186, 0.49)",
    "rgba(149, 199, 143, 0.49)",
    "rgba(229, 86, 61, 0.49)",
    "rgba(212, 229, 61, 0.49)",
    "rgba(206, 61, 229, 0.49)",
    "rgba(229, 157, 61, 0.49)",
    "rgba(61, 165, 229, 0.49)",
    "rgba(61, 229, 133, 0.49)",
    "rgba(229, 61, 61, 0.49)",
    "rgba(116, 61, 229, 0.49",
    "rgba(218, 229, 61, 0.49)",
    "rgba(21, 43, 157, 0.49)",
    "rgba(153, 157, 21, 0.49)",
    "rgba(199, 143, 186, 0.49)"
    // "rgba(149, 199, 143, 0.49)"
  ];
  
  var container = $("<div/>", {
    "id":"container",
    "title":"click to pause , resume `scroller`"
  });
  
  var n = 7;

  var scrolled = false;

  var elems = $.map(colors, function(color, i) {
    return $("<div>", {
      "class": "blocks-" + (i + 1),
      "text": i + 1,
      "css": {
        "backgroundColor": color,
        "left": (i * 25) + "%"
      }
    })[0].outerHTML
  });
  
  var scroller = function scroller(e) {

    var xz = container.scrollLeft();
    
    var wx = container.width() * .73;
    
    var keys = $.map($("div:first, div:last", container), function(el) {
      return Number(el.className.replace(/[^\d+]/g, ""));
    });
    
    var first = keys[0];
    
    var last = keys[1];
    
    var _scroller = function _scroller(elem, idx) {

      if (idx === 1 && scrolled) {
        scrolled = false;
        elem.scroll()
        return
      };
      
      elem
      .stop(true, true)
      .off("scroll")
      .empty()
      .append(
        elems
        .slice(idx ? (idx - 1) - n : last
               , idx ? idx - 1 : last + n)
        .map(function(el, i) {
           return $(el).css("left", (i * 25) + "%")[0].outerHTML
        }).join("")
      )
      .delay(250)
      .animate({
          scrollLeft: (idx ? "+=" : "-=") + elem.width() / 3
        }, 1000, function() {
            scrolled = true;
            elem.on("scroll", scroller).scroll()
      })      
    };
        
    if (xz < n && first !== 1 ) {
      _scroller(container, first)
    };
    
    if (xz > wx && last !== colors.length) {              
       _scroller(container);   
    };
    


  };

  $("body")
  .append(
    container.data("scroll", true).html(elems.slice(0, n))
    .on("scroll", scroller)
  );
  
  container.on("click", function() {
     if (container.data("scroll")) {
       container.data("scroll", false).off("scroll")
     } else {
       container.data("scroll", true)
       .on("scroll", scroller).scroll()
     }
  })

});
[class^=blocks] {
  padding: 0px;
  width: 25%;
  position: absolute;
  height: 150%;
  text-align: center;
  font-size: 100px;
}
#container {
  width: 100%;
  height: 250px;
  position: relative;
  overflow: auto;
  margin-top: 50%;
  background: #2a2a2a;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>



回答4:

Actually in the example you have given you are scrolling through divs. Each div contains a table, but that's beside the point.

I would say you need one content div, and play with its margins in javascript according to current scroll position. Just make sure the total width remains the same.

This would ensure the standard scrollbar is shown correctly and doesn't "jump", while the content can be adjusted to its position.

In the example you provided, BTW, they use 3 divs with partial content - only the div or divs in display have content, and perhaps partial content (only some content on its bottom or top, enough to fill the displayed area).

Anyway, the key here is to maintain a scrollable element large enough for the scrollbar to maintain its size and position, or to use a custom scrollbar written completely in javascript.