如何将Jquery滚动事件绑定到动态添加的元素?

3
我将创建一个Ember组件,并需要将滚动事件绑定到动态创建的div上。
我的handlebars代码如下:
<div class="searchbarContainer" tabindex="0" bubbles=false>
    {{input type="search" name="searchFor" class="searchTextField" placeholder="Search..." value=searchKey}}

    {{#if searchKeyNotNull}}
    <div class="searchResultsContainer box-shadow" {{bind-attr id="searchBarID"}}>
        {{!-- BINDS ID TO searchBarID for searchResultsContainer--}}
            {{#if noResults}} {{!-- Then say : "No Results. " --}}
                      <div class="applicantsName noResults">
                        No Results found.
                      </div>      
            {{else}}
                {{!-- For each Row, include visual elements. --}} 
                {{#each toSearch }}
                    <div> ... </div>
                {{/each}}
            <div class='endOfResults'>
                End of Results
            </div> 
            {{/if}}
    </div>
    {{/if}}
</div>

根据输入部分的内容,逻辑被设置为更新searchKeyNotNull为'true'或'false',noResults也是如此。

目前,searchResultsContainer div中填充有包含结果的div,并且有最大高度。我还启用了overflow,以便用户可以滚动查看结果。

我需要将滚动事件绑定到此div,以便当它达到末尾时,触发某个操作。

现在,这是我开始使用的事件代码:

        $('#searchBarID').bind('scroll',function(){
        if($(this).scrollTop() + $(this).innerHeight()>=$(this)[0].scrollHeight)
        {
          alert('end reached');
        }
      });

哪个方法可以解决问题。不幸的是,当运行此代码片段时,div尚未创建,因此此事件永远不会绑定。我还尝试使用jQuery.on函数,但似乎滚动不会冒泡,因此

        $('body').on('scroll','#searchBarID', function(){
        if($(this).scrollTop() + $(this).innerHeight()>=$(this)[0].scrollHeight)
        {
          alert('end reached');
        }
      });

无法工作。

我已经尽力寻找解决方法了,我需要: a)找到一种将滚动条绑定到动态添加元素的方法,或者 b)找出Ember何时创建元素,以便我可以立即插入此绑定代码。

任何一种解决方案都会对我有很大帮助!


请查看didInsertElement钩子:https://dev59.com/vmox5IYBdhLWcg3w74q3 - Oliver
什么导致具有id searchBarID的元素被添加到DOM中?我不熟悉Ember,但是在面对类似问题时,我会尝试将回调附加到创建searchBarID的任何内容上,以确保事件绑定跟随DOM节点的创建。 - J E Carter II
我确实考虑过那种方法:不幸的是,我找不到关于DOM元素何时以及如何创建的好文档。谢谢! - Darshan
Oliver,我确实检查了这个钩子函数,它帮助我澄清了其他我还没有询问的问题,但对于这个特定情况,didInsertElement只会被触发一次,在此之后DOM元素会被多次重新创建。因此很遗憾它在这种情况下不适用。 - Darshan
@Darshan,“didInsertElement”钩子函数绝对是你要找的。你只需要稍微修改一下你的模板即可。 - Matthew Blancarte
@MatthewBlancarte,我曾经固执地尝试让.on和scroll应用于动态元素,并遇到了不必要的错误。我同意你的看法:无疑最好的做法是你和Jordy建议的。我会调查一下并在看到进展后及时更新。 - Darshan
3个回答

1
我曾经遇到过这个问题。我发现最简单的方法是隐藏元素,而不是将其放置在if块中。
您可以通过扩展绑定属性来实现。
<div class="searchResultsContainer box-shadow" {{bind-attr id="searchBarID" class="searchKeyNotNull::hidden"}}>

hidden 是一个将 display 设置为 none 的类。这会导致元素仅渲染一次,并允许您使用 didInsertElement 钩子,如下所示:

onDidInsert: function () {
    var element = $('#searchBarID', this.$());
    element.on('scroll', function () {
        // your impl.
    })
}.on('didInsertElement')

在你的组件中。
我使用 bind-attr 来隐藏元素(而不是不渲染它!)的原因是因为 Ember 每次 searchKeyNotNull 改变时都会创建一个新元素。然而,didInsertElement 钩子只会触发一次。

嗯,绝对是一个不错的建议:当我先创建它,然后进行修改时,似乎有些东西出了问题。我会尽快更新这个评论,确认它是否可行。 - Darshan

0
大多数情况下,可以使用Ember视图钩子、动作和事件来完成通常在jQuery中执行的任务。
另一种方法是将DOM节点包装在{{#view}}中,然后利用didInsertElement钩子。例如,让我们将其包装在{{#view App.SearchResultsView}}中:
<div class="searchbarContainer" tabindex="0" bubbles=false>
    {{input type="search" name="searchFor" class="searchTextField" placeholder="Search..." value=searchKey}}

    {{#if searchKeyNotNull}}
      {{#view App.SearchResultsView}}
        <div class="searchResultsContainer box-shadow">
          <!-- MORE HBS HERE -->
        </div>
      {{/view}}
    {{/if}}
</div>

现在,让我们定义 App.SearchResultsView:

App.SearchResultsView = Ember.View.extend({
  didInsertElement: function () {
    this.$('.searchResultsContainer').on('scroll', function (e) {
      // scrolling...
    });
  }
});

希望这有所帮助!如果需要进一步解释,请告诉我。


等等,等等,等等:我对Ember还比较新,但你是在告诉我我可以将组件与视图关联起来吗?我的意思是,如果是这样的话——哇——每次dom元素改变时,视图都会被重新创建,每次视图被重新创建时,didInsertElement都会被触发,因此每次需要绑定滚动时,它就会被绑定在那里。我必须尽快尝试这条路线。稍后会更新。 - Darshan
啊-我遇到了一个问题。似乎一旦它进入视图,视图就无法再访问SearchBarComponent中定义的方法。因此,尽管滚动绑定起作用,但由于我无法访问组件的逻辑部分,我无法调用所需的函数。除非我在这里漏掉了什么... - Darshan
是的,你缺少一些东西。 :) 你可以从组件和视图中都访问当前控制器……this.get('controller'),并且你可以将Ember.Evented作为一个 mixin 与该控制器一起使用,以根据需要进行通信。您可以让视图滚动事件调用控制器方法,在视图中触发某些内容。http://emberjs.com/api/classes/Ember.Evented.html 让你的控制器去“思考”,只需让你的视图发送消息即可。希望这有意义。 - Matthew Blancarte
我确实尝试过那种方法 - 但不幸的是,在我的情况下,这是一个可以访问存储库(该访问是通过传递进行的)的组件,因此操作位于App.SearchBarComponent而不是数组/对象控制器中。我尝试传递controller:'searchBarComponent',但我怀疑它使用命名约定查找控制器。嗯...我现在已经实现了Jordy的方法,但我想探索视图方法。有什么想法吗?如果这拖延了时间,我深表歉意。 - Darshan
@Darshan 嗯?组件是自定义标签视图...我想你可能误解了我的意思。您的视图和组件都可以访问 this.get('controller')。试试看。console.log(this.get('controller').toString()) - Matthew Blancarte
显示剩余2条评论

0

我知道它不会冒泡;这就是为什么我在问,是否有可能在元素创建时绑定滚动,并且为此我需要知道Ember何时以及如何创建DOM元素。我想知道是否有人能够为我提供答案。 - Darshan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接