【问题标题】:Adding an eventlistener on not yet generated DOM在尚未生成的 DOM 上添加事件监听器
【发布时间】:2022-01-26 03:19:57
【问题描述】:

我有一堆

<div class="location-box" data-location-id="123">
  <img src="img_url" />
</div>

加载到我的.locations div 中。 我希望每当您单击 .location-box 时,单击的 div 会在其上突出显示类。并且属性值被添加到隐藏的输入中。当您单击另一个时,前一个中的类将被删除。以此类推。

我以前试过当那些 div 是静态的时,它工作得很好。但现在我从 api 调用中将这些 div 附加到纯 Javascript 之外。

我也知道尚未生成的 DOM 不能被事件监听器等操作。

我研究了变异观察者,并尝试了文档中的一些简单内容。但我可以让这段代码使用它

let locations = document.querySelectorAll(".location-box");

locations.forEach( el => 
    el.addEventListener('click', function() {
        locations.forEach( els => els.classList.remove('active-location'));
        document.getElementById('location_id').value = this.getAttribute('data-location-id');
        this.classList.add("active-location");
    })
);

有谁知道如何进行这项工作?也许不仅是这一次,而且在许多情况下。因为在不久的将来我可能会有更多尚未生成的 DOM。

【问题讨论】:

  • 你真的应该在呈现 HTML 的代码中处理它。也许在你“在 api 调用中使用 javascript 附加 div”时显示该代码。
  • @skara9 嗨,我试过这个``` document.querySelector('.locations').addEventListener('click', function(e){ if(e.target.className == 'location -box'){ 让位置 = document.querySelectorAll('.location-box'); locations.forEach(el => { el.classList.remove('active-location'); }); e.target.classList. add('active-location'); } });``` 它之前在另一个元素上的工作类似,但这次不是
  • 好吗?看不出这和我说的有什么关系
  • @Coolguy31 ... 基于MutationObserver 的方法很可能是矫枉过正。 Event Delegation 可能是首选技术。但是为了以某种方式正确地实现它,很高兴知道所有后来渲染的东西是否总是插入/附加到一个公共且已知的根节点下方,因为document.body 因为事件监听根也不是最优雅/高性能的选择.
  • @Coolguy31 ...关于提供的答案/解决方案/方法还有什么问题吗?

标签: javascript html dom


【解决方案1】:

根据我上面的评论...

"@Coolguy31 ...基于MutationObserver 的方法很可能是矫枉过正。Event Delegation 可能是选择的技术。但是为了以某种方式正确地实现它,很高兴知道以后是否渲染的东西总是插入/附加在一个常见的也已知的根节点下面,因为document.body 作为事件监听根节点也不是最优雅/性能最好的选择。”

function uuid(a) {
  // [https://gist.github.com/jed/982883] - Jed Schmidt
  return a
    ? (a^Math.random()*16>>a/4).toString(16)
    : ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,uuid);
}

function addLocations(evt) {
  evt.preventDefault();

  const allLocationsRoot = 
    document.querySelector('.locations');

  allLocationsRoot.innerHTML = `
    <div class="location-box">
      <img src="https://picsum.photos/133/100?grayscale" />
    </div>
    <div class="location-box">
      <img src="https://picsum.photos/100/75?grayscale" />
    </div>
    <div class="location-box">
      <img src="https://picsum.photos/120/90?grayscale" />
    </div>
  `;
  allLocationsRoot
    .querySelectorAll('.location-box')
    .forEach(locationNode => locationNode.dataset.locationId = uuid());

  allLocationsRoot
    .closest('form[name="location-data"]')
    .elements['location']
    .value = '';
}
function initializeAddLocations() {
  document
    .querySelector('button')
    .addEventListener('click', addLocations);
}

function handleLocationSelect(evt) {
  const locationItemRoot = evt
    .target
    .closest('.location-box');

  if (locationItemRoot) {

    const allLocationsRoot = locationItemRoot
      .closest('.locations');

    const locationControl = allLocationsRoot
      .closest('form[name="location-data"]')
      .elements['location'];

    // console.log({
    //   target: evt.target,
    //   locationItemRoot,
    //   allLocationsRoot,
    //   locationControl,
    // });
    allLocationsRoot
      .querySelectorAll('.location-box')
      .forEach(locationNode => locationNode.classList.remove('selected'));

    locationItemRoot.classList.add('selected');
    locationControl.value = locationItemRoot.dataset.locationId;
  }
}
function initializeLocationHandling() {
  document
    .querySelector('.locations')
    .addEventListener('click', handleLocationSelect)
}

function main() {
  initializeLocationHandling();

  initializeAddLocations();
}
main();
body { margin: 0; }
[type="text"][name="location"] { width: 23em; }
.locations:after { display: block; content: ''; clear: both; }
.location-box { float: left; margin: 4px; padding: 10px; min-height: 104px; background-color: #eee; }
.location-box.selected { outline: 2px solid #fc0; }
<form name="location-data">

  <div class="locations">
  </div>
  <button>update locations</button>

  <!--
  <input type="hidden" name="location" />
  //-->
  <input type="text" name="location" disabled />

</form>

【讨论】:

    【解决方案2】:

    您可以使用MutationObserver 来做到这一点,代码如下所示,它没有获取属性的部分,但您可以添加它,另一种方法就像@scara9 所说,在用于呈现每个 .location-box 的代码上,您可以分配点击处理程序。

    在下面的代码中,我使用 jquery 来“添加”新的location-box,你不需要 jquery 来做这个

    // select the parent node: .locations, i switched to ID for test
    var target = document.getElementById("locations");
    
    // create an observer instance
    var observer = new MutationObserver(function (mutations) {
      //loop through the detected mutations(added controls)
      mutations.forEach(function (mutation) {
        //addedNodes contains all detected new controls
        if (mutation && mutation.addedNodes) {
          mutation.addedNodes.forEach(function (elm) {
            if (elm && elm.className=="location-box") {
              elm.addEventListener("click", function () {
                elm.classList.add("active-location");
    
                var locations = document.querySelectorAll(".location-box");
    
                locations.forEach((e) => {
                  if (e != elm) {
                    //ignore clicked element
                    e.classList.remove("active-location");
                  }
                });
              });
            }
          });
        }
      });
    });
    
    // pass in the target node, as well as the observer options
    observer.observe(target, {
      childList: true
    });
    
    //you don't need this, it's only to simulate dynamic location-box
    $(function () {$("button").on("click", function () {var count = $(".location-box").length + 1;$(".locations").append($("<div class='location-box' data-attribute-id='" +count +"'>location box:" +count +"</div>"));});});
    .active-location{
      background-color: yellow;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div class="locations" id="locations">
    
    
    </div>
    
    <!-- This button it's only to add the controls, not needed in your case-->
    <button type="button" id="add">
    Add
    </button>

    【讨论】:

    • 是的,这就是我想要的!系统怎么知道只在 .location-box btw 上添加该类?
    • 它只查看.locations 容器,如果您在.location-box 的同一级别添加其他元素,那么您可以在if (elm) 上包含类名过滤器来查看它,我更新了我的答案
    猜你喜欢
    • 2017-09-10
    • 2020-06-13
    • 1970-01-01
    • 2014-05-11
    • 2014-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多