处理JavaScript中按钮点击的最佳方式(香草)

时间:2017-10-13 14:50:42

标签: javascript events addeventlistener target

编辑:我的问题可能过于广泛(仍在学习),但我收到了非常有用的答案。非常感谢你们的投入。我还修改了我的代码,以便将传递事件'事件'。

包含在内

这是我在这里提出的第一个问题,所以我有点紧张......

我们开始:管理按钮点击并让它们触发不同功能的最佳方法(关于最佳实践,速度,兼容性等)是什么?

在研究这个问题时,我在Eloquent JavaScript上找到了一个例子并将其作为基础。

然后我通过使用“mapper”(这是正确的术语吗?)来扩展它,以找到正确的函数,具体取决于触发事件的按钮的id。

以下是代码:



// functions I want to trigger
function one(e) {
  alert("You clicked " + e.textContent);
}

function two(e) {
  alert("You clicked " + e.textContent);
}

// mapper object to hold my functions
var buttonMap = {
  b_one: function(event) {
    one(event.target)
  },
  b_two: function(event) {
    two(event.target)
  }
}

// Event Listener
var buttonClick = document.body.addEventListener('click', function(event) {
  if (event.target.nodeName == "BUTTON") {
    var tar = event.target.id;
    buttonMap[tar](event)
  }
});

<button id="b_one" type="button">Button One</button>
<button id="b_two" type="button">Button Two</button>
&#13;
&#13;
&#13;

这个解决方案好吗?怎么改进?我可能遇到任何问题吗?

小小的奖励问题:此外,我还考虑将整个事情包裹在document.addEventListener("DOMContentLoaded", function() {})内。

这是个好主意还是需要?在什么情况下我应该检查是否加载了DOM内容以及何时可以省略此检查?

4 个答案:

答案 0 :(得分:2)

您已经创建了一个简单的事件委派,使您能够添加/删除按钮,而无需添加/删除事件处理程序。

如果您有很多动态创建或销毁的按钮,最好使用事件委派,而不是支付手动管理处理程序的成本。

您可以将事件处理代码放在正文的末尾,而不是使用DOMContentLoaded事件。只要容器(在这种情况下为body)存在,子项是否存在就没有关系。

将全局事件处理程序附加到最近的容器,如果可以,则不附加到正文。此外,使用data-*属性而不是idvalue属性将数据添加到按钮。 id创建一个全局变量,值将在表单中使用。

<div class="buttonsContainer">
  <button type="button" data-value="one">Button One</button>
  <button type="button" data-value="two">Button Two</button>
</div>

您应该将事件对象传递给事件处理程序:

var buttonClick = container && container.addEventListener('click', 
    function(event) {
      var target = event.target;
      var handler;
      if (target.nodeName == "BUTTON" && (handler = target.getAttribute('data-handler'))) {
        buttonMap[handler](event)
      }
    });

演示:

function one(e) {
  alert("You clicked " + e.textContent);
}

function two(e) {
  alert("You clicked " + e.textContent);
}

// mapper object to hold my functions
var buttonMap = {
  b_one: function(event) {
    one(event.target)
  },
  b_two: function(event) {
    two(event.target)
  }
}

// Event Listener
var container = document.querySelector('.buttonsContainer');
var buttonClick = container && container.addEventListener('click', function(event) {
  var target = event.target;
  var handler;
  if (target.nodeName == "BUTTON" && (handler = target.getAttribute('data-handler'))) {
    buttonMap[handler](event)
  }
});
<div class="buttonsContainer">
  <button type="button" data-handler="b_one">Button One</button>
  <button type="button" data-handler="b_two">Button Two</button>
</div>

答案 1 :(得分:1)

除了添加可能在动态DOM环境中有用的抽象层之外,mapper函数实际上并没有增加太多内容。

但是,如果您在映射器中知道哪个功能与哪个按钮一起使用,则可以直接使用相应的按钮注册这些功能。

另外,请注意,当您将匿名函数注册为事件回调时,您将无法使用.removeEventListener()取消注册回调。这并不意味着对事件使用匿名函数是一个交易破坏者,但这是需要考虑的事情。

请注意,您在评论中解释了几个HTML和JavaScript问题:

document.getElementById("b_one").addEventListener("click", one);
document.getElementById("b_two").addEventListener("click", two);

// The e parameter represents the event object itself. It won't
// have any textcontent. But, in a DOM event handler, "this" will
// refer to the element that triggered the event and that's where
// the textContent is.
function one(e) {
  alert("You clicked " + this.textContent);
}

function two(e) {
  alert("You clicked " + this.textContent);
}
<!-- You typically won't give a <button> element a "value"
     attribute. The content within the element is usually 
     what you need. -->
<button id="b_one" type="button">Button One</button>
<button id="b_two" type="button">Button Two</button>

现在,如果你的回调函数需要参数,你需要将它包装在另一个成为主回调的函数中,然后该函数可以调用你的实际函数,向它发送它可能需要的参数,如:

document.getElementById("b_one").addEventListener("click", function(evt) {
  // The actual callback will automatically recieve a reference to the
  // event that triggered it, so it's a good idea to pass that along
  // to the main function so that the DOM object and details about the 
  // event can be used along with any other custom arguments you require.
  one(evt, new Date().toLocaleTimeString()); 
});

function one(e, d) {
  console.log("The event was: " + e.type);
  console.log("It happened at: " + d);
  console.log("The text of the element was: " + e.target.textContent);
}
<!-- You typically won't give a <button> element a "value"
     attribute. The content within the element is usually 
     what you need. -->
<button id="b_one" type="button">Button One</button>

答案 2 :(得分:1)

你的方法很好,但它的工作方式与现在的方式不同。映射函数使用event.target调用处理程序,但在调用映射函数时,永远不会传递event。映射函数需要看起来像这样才能工作:

...
b_one: function(event) {
  one(event.target)
}
...

...并且您在事件处理程序中对它的调用需要如下所示:

buttonMap[tar](event)

您正在做的事情,将事件监听器附加到正文并根据事件目标调用处理程序称为事件委派。您应该添加一个检查以确保单击的按钮确实具有关联的映射函数,以确保在单击其他按钮时它不会崩溃:

if (typeof buttonMap[tar] === 'function') {
  buttonMap[tar](event);
}

DOMContentLoaded上运行代码的目的是确保在解析DOM并且可用之后代码运行。这也可以通过将脚本块放在它所依赖的DOM元素之后来实现。例如,在body元素的末尾。

答案 3 :(得分:-1)

为了完整起见,您还可以考虑使用onClick HTML属性。

&#13;
&#13;
function myFunction() {
  alert('hey!');
}
&#13;
<button onClick="myFunction()">Do something</button>
&#13;
&#13;
&#13;

这可能是旧时尚,但它很容易理解和维护,而且现在正如React *这样的图书馆所发生的事情。

*:嗯,不是真的,但API类似。