0

UPDATE: The below code does not work the way I wanted to, like as I mention below, it should show five items at a time when the user clicks on the button.

I'm trying to use javascript slice method (please suggest if this is not the right way to use), the array list show five array item at a time and I have created the codepen example to show what I'm trying to do

Let's assume I have 20 records, if the user click on first time, I should be getting 1-5 array items if the user click on second time, I should be getting 5-10 .....so on and so forth.

https://codepen.io/TLJens/pen/NPZyYR

The code here:

$('#loading').hide();

var counts = 0;
var displayCount = 5;
var starting = 0;
var data = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16];

function slicemydata(sstart, totalsize, eend) {  
    var items = []; debugger;
    if (totalsize <= data.length) {
        if (eend == 0) {
          items = data.slice(sstart,totalsize);
        } else { 
          if (sstart > eend) {
            eend = data.length;
          }
          items = data.slice(sstart, eend);
          sstart =  displayCount + 5;
        }
    }
    console.log(items);
  $('.js-lazy-load-data').append(items);
}

$('.js-lazy-load').click(function () {
 
  counts++;
  slicemydata(starting,data.length,displayCount);
 
    $('.js-lazy-load').fadeOut();
  
    // Minor timeout before showing loader
   // setTimeout(function () {
   //     $('#loading').fadeIn();
   // }, 400); 
  
    // Simulate server call by showing loading gif  
    // for 2.5 seconds before displaying results
    //setTimeout(function () {
    //    $('#loading').fadeOut();
    //}, 2600); 
  
    // Display results after 3 seconds
    setTimeout(function () {
        //$('.js-lazy-load-data').append(data);
        $('.js-lazy-load').show();
    }, 1000);  
});
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
  • I'm trying to figure out what exactly you're asking here. Is something not working as you'd expect? Are you getting an error? What do you need help with? – mhodges Apr 20 '22 at 16:58
  • updated little bit more hope this is clear – ThreeOneThree Apr 20 '22 at 17:03
  • You say you want to "Show 5 items at a time", but the example appends 5 items to the existing list. Are you wanting to only show 5 at a time like pagination, or are you wanting to append 5 more items to the existing list every time the "more" button is pressed? – mhodges Apr 20 '22 at 17:04
  • it should not append rather it should show the new 5 items and I forgot to comment that line, so it's like pagination or you can say lazy loading – ThreeOneThree Apr 20 '22 at 17:08
  • Ah, okay. Understood. I'll write up a solution based on what you have – mhodges Apr 20 '22 at 17:14
  • I see several issues in your code that may cause "unexpected" slicing issues. First, when using `totalsize` to slice, you probably actually want to return `items = data.slice(sstart, sstart + totalsize)`. Secondly, if `sstart > eend` you assume `sstart` is also less than `data.length` by returning `data.slice(sstart, data.length)`. Also, the `slicemydata` function will always set `sstart` to 10 because `displayCount` always equals 5. – jacob Apr 20 '22 at 17:15

3 Answers3

2

This might be a use case for Generators and generator functions; the OP's task at least makes a good practical exercise ...

function* createChunkGenerator(
  itemList, chunkSize = 1, chunkCount = 0
) {
  // sanitize and decouple (shallow copy) the passed
  // array reference, thus one can `splice` the list
  // without mutating the original array.
  itemList = Array.from(itemList ?? []);
  chunkSize = Math.max(chunkSize, 1);

  while (itemList.length >= 1) {
    ++chunkCount;

    yield {
      chunkCount,
      itemList: itemList.splice(0, chunkSize),
    };
  }
}


let chunkGenerator = createChunkGenerator(
  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], 5
);
let chunks;

console.log('...automatically tiggered `next` based iteration...');

while (chunks = chunkGenerator.next().value) {
  const { chunkCount, itemList } = chunks;

  console.log({ chunkCount, itemList });
}


chunkGenerator = createChunkGenerator(
  [1, 2, 3, 4, 5, 6, 7, 8], 6
);
console.log('...explicitly (e.g. event) triggered `next` based iteration...');
console.log(
  chunkGenerator.next()
);
console.log(
  chunkGenerator.next()
);
console.log(
  chunkGenerator.next()
);
console.log(
  chunkGenerator.next()
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

The below DOM / DOM event implementation demonstrates the handy usage of a generator based paginator/pagination.

function* createChunkGenerator(
  itemList, chunkSize = 1, chunkCount = 0
) {
  // sanitize and decouple (shallow copy) the passed
  // array reference, thus one can `splice` the list
  // without mutating the original array.
  itemList = Array.from(itemList ?? []);
  chunkSize = Math.max(chunkSize, 1);

  while (itemList.length >= 1) {
    ++chunkCount;

    yield {
      chunkCount,
      itemList: itemList.splice(0, chunkSize),
    };
  }
}

function handleCreateLoadItemsFromBoundData({ currentTarget }) {
  const { generator: chunkGenerator, elmOutput } = this;
  const chunks = chunkGenerator.next().value ?? null;
  
  if (chunks !== null) {
    const { chunkCount: loadCount, itemList: loadItems } = chunks;

    elmOutput.value =
      `... loadCount: ${ loadCount }, loadItems: ${ loadItems } ...`;
  } else {
    elmOutput.value =
      '... no more load items ...';

    currentTarget.disabled = true;
  }
}
document
  .querySelector('button')
  .addEventListener(
    'click',
    handleCreateLoadItemsFromBoundData.bind({
      generator: createChunkGenerator(
        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], 5
      ),
      elmOutput: document.querySelector('output'),
    })
  );
<button>load items</button>
=>
<output>...</output>
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
  • Hi Peter, I'm trying to implement lazy loading not pagination – ThreeOneThree Apr 21 '22 at 17:24
  • @ThreeOneThree ... Good, and it doesn't even change the above approach/implementation since the task stays the same ... _**slicing/splicing the specified `nth` items from an array at time**_. A configurable generator is not the worst solution to it. Thus the only thing left for me was to change/adapt the wording. – Peter Seliger Apr 21 '22 at 17:29
  • yes, I understand, so I change the array to array object and the code does not work, I see that createPaginator expecting an array `[]` so I changed to `{}`, anything else I need to modify? – ThreeOneThree Apr 21 '22 at 17:50
  • I don't know what ... _"so I change the array to array object"_ ... actually means. Since the OP's main tasks is ... **slicing array list five elements at a time** ... the generator function `createChunkGenerator` expects two parameters being passed, 1st the array (it doesn't matter what the array contains) one wants to slice / splice / get chunks from, 2nd the (optional) chunk size value which preferably should be grater than or equal to `1`. – Peter Seliger Apr 21 '22 at 17:56
  • Thanks for patience with me and I'm not going to ask another question instead I will create a new one, so you don't get annoy with me :) – ThreeOneThree Apr 21 '22 at 21:26
1

Here's how I would approach it.

  1. Pass in the data and the element that needs to be updated to a function.

  2. That function initialises the index, and returns a new function that acts as the handler for the listener.

  3. Within the body of that function you do the work of writing the new HTML using the sliced data, and then updating the element.

const data = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16];

// Cache the elements
const div = document.querySelector('div');
const button = document.querySelector('button');

// Call `slicer` with the data, and the element to be updated.
// `slicer`returns a new function that is assigned to the listener
button.addEventListener('click', slicer(data, div), false);

// `slicer` accepts some data, and the 
// element to be updated
function slicer(data, div) {

  // Initialises the `index` which is
  // scoped to the returning function. No
  // need for global variables!
  let index = 0;

  // Returns a function that keeps a record
  // of index so it can update it
  return function () {

    if (index < data.length) {
      
      // `slice` the data from the current
      // index, `map` over that array to create
      // some HTML, and then join it up
      const html = data
        .slice(index, index + 5)
        .map(el => `<span>${el}</span>`)
        .join(', ');

      // Add that to the element that needs updating
      div.innerHTML = `<div>${html}</div>`;
      
      // Finally increase the index
      index += 5;
    
    } else {
    
      console.log('No more data');
    
    }
      
  }

}
<button>Click me!</button>
<div></div>

Additional documentation

Andy
  • 61,948
  • 13
  • 68
  • 95
0

So I think if all you're wanting to do is paginate the data, you can do it much simpler by just keeping track of the current "pageIndex" you're on and how long each "page" is. That way, your start position in the array is just pageIndex * pageSize and your end position in the array is just start + pageSize. Then all you have to do to get the next or previous page is just increment/decrement your page index accordingly. I'll leave the exercise of displaying the data to you since that's not relevant to what you needed help with. Hope this helps!

let data = [1,2,3,4,5,6,7,8,9,10,11,12,13];
let currentPage = 0;
let pageSize = 5;
function getPage(data, pageSize, pageIndex) {
  let start = pageSize * pageIndex;
  return data.slice(start, start + pageSize) 
}

console.log(getPage(data, pageSize, currentPage));
currentPage++;
console.log(getPage(data, pageSize, currentPage));
currentPage++;
console.log(getPage(data, pageSize, currentPage));
currentPage++;
console.log(getPage(data, pageSize, currentPage));
currentPage++;
mhodges
  • 10,938
  • 2
  • 28
  • 46