ptf
ptf

Reputation: 3370

Need a way to filter divs based on user input and at the same time keep event binding on them

At the moment I'm filtering an array with array.filter, passing in a regex. The array I am filtering is based on the contents of a div.

Each child div of that div has a text which I extract with $.text() and put into its own array so I can filter it. Then I output the contents of the filtered array.

The problem is that the filtering needs an array of strings, and thus I make separate array which I filter and then print to the html. But, then when a user clicks one of the items in the list, nothing happens, because the user clicks on div which isn't bound to an event. I do the binding on document ready, and I use the bound event to get the value of the data-item-pageid attribute.

I need a way to this which gives me access to the data-item-pageid of the clicked element and still is fast. I would prefer not to bind the events all over again each time the user types, so I think the logic needs to be changed. I need to hide the divs that do not match the regex.

this is the html:

<div class="content">
    <div class="categories">
        <div class="item" data-item-pageid="1">text1</div>
        <div class="item" data-item-pageid="2">text2</div>
        <div class="item" data-item-pageid="3">text3</div>
    </div>
    <div class="categories">
        <div class="item" data-item-pageid="4">text4</div>
        <div class="item"data-item-pageid="5">text5</div>
        <div class="item"data-item-pageid="6">text6</div>
    </div>
</div>
<div class="filter_result"></div>

This is the JavaScript code:

// Binds the click event for each category item
$('.category_item').click(function() {
   console.log($(this));
});

...

// Array for filter
filterArray = [];
for (var i = 0; i < $categoryItems.length; ++i) {
    filterArray[i] = $($categoryItems[i]).text();
}

...

function filterList(key) {

    var result = filterArray.filter(/./.test.bind(new RegExp(key, 'i')));

    if (key != '' || key) {
        var markup = []; //ugh
        for (var i = 0; i < result.length; ++i) {
            markup += '<div class="category_item">' + result[i] + '</div>';
        }

        if ($categoryItems.is(':visible'))
            $categoriesDiv.toggle();

        $filteredResult.html(markup);
    } else {
        $filteredResult.html('');

        if (!$categoryItems.is(':visible'))
            $categoriesDiv.toggle();
    }
}

Upvotes: 0

Views: 196

Answers (1)

Matyas
Matyas

Reputation: 13702

You could do delegated event handling

$(CONTAINER_SELECTOR).on('click', CHILD_SELECTOR, function(){ 
     /* Your click handler*/ 
});
  • Where CONTAINER_SELECTOR is an element that is not added/removed so it retains its listeners
  • CHILD_SELECTOR is the selector for the children which are added/removed, of whose clicks we wish to listen to

In your case $(CONTAINER_SELECTOR) could be $filteredResult and CHILD_SELECTOR .category_item.

For better understanding of delegated events check out the official docs or have a look at one of my other answers on delegated events.

UPDATE

You could easily retrieve the filtered list using:

$elements = $('.item');
$filteredList = $elements.filter(
     function(index){ 
         return conditionIsMetFor($(this).text()); // this refers to the element under iteration
     }
);

Then you cant take $filteredList and:

  • $.clone() it
  • iterate over it and create new elements based on the $.data() they contain
  • etc.

Upvotes: 1

Related Questions