如何重构此JavaScript代码以避免在循环中创建函数?

时间:2010-05-26 05:21:26

标签: javascript function loops closures event-delegation

我为我正在处理的项目编写了以下代码:

var clicky_tracking = [
  ['related-searches', 'Related Searches'],
  ['related-stories', 'Related Stories'],
  ['more-videos', 'More Videos'],
  ['web-headlines', 'Publication']
];

for (var x = 0, length_x = clicky_tracking.length; x < length_x; x++) {
  links = document.getElementById(clicky_tracking[x][0])
                  .getElementsByTagName('a');
  for (var y = 0, length_y = links.length; y < length_y; y++) {
    links[y].onclick = (function(name, url) {
      return function() {
        clicky.log(url, name, 'outbound');
      };
    }(clicky_tracking[x][1], links[y].href));
  }
}

我要做的是:

  • 定义二维数组,每个实例包含两个元素的内部数组:id属性值(例如,“related-searching”)和相应的描述(例如,“Related Searches”);
  • 对于每个内部数组,找到document中具有相应id属性的元素,然后收集其中所有<a>个元素(超链接)的集合; < / LI>
  • 循环遍历该集合并将onclick处理程序附加到每个超链接,该超链接应调用clicky.log,将参数传递给与id对应的描述(例如,“相关搜索” “对于id”相关搜索“)以及被点击的href元素的<a>属性的值。

希望这并不是完全令人困惑的!代码可能比那更容易解释。

我相信我在这里实现的是一个闭包,但JSLint抱怨:

http://img.skitch.com/20100526-k1trfr6tpj64iamm8r4jf5rbru.png

所以,我的问题是:

  • 如何重构此代码以使JSLint合适?或者,更好的是,有没有一种最佳实践方法来实现这一点,我不知道JSLint的想法是什么?
  • 我应该依赖事件委派吗?也就是说,将onclick事件处理程序附加到我的数组中具有document属性的id元素,然后查看event.target?我之前已经做过这个并且理解了这个理论,但我对细节非常模糊,并且会对这看起来像什么有所了解 - 假设这是一个可行的方法。

非常感谢您的帮助!

3 个答案:

答案 0 :(得分:1)

这段代码不太可维护!我讨厌把jQuery推到你的喉咙,但这看起来像是对你非常有用的情况,例如:

$('a.trackable').click(function(e) {

  clicky.log(url, name, 'outbound');
};

您可以通过向每个链接添加“trackable”类来启用跟踪,并使用例如$(this).attr('rel')将链接映射到查找表。

希望这是有道理的。

答案 1 :(得分:1)

您可以创建一个函数来构建事件处理程序,例如:

function createClickHandler(url, name) {
  return function () {
    clicky.log(url, name, 'outbound');
  };
}

for (var x = 0, length_x = clicky_tracking.length; x < length_x; x++) {
  links = document.getElementById(clicky_tracking[x][0]) // NOTE:links should be 
                  .getElementsByTagName('a');            // declared at the top
  for (var y = 0, length_y = links.length; y < length_y; y++) {
    links[y].onclick = createClickHandler(clicky_tracking[x][1], links[y].href);
  }
}

我也认为event delegation也是一个非常好的选择,你可以很容易地实现它:

var clicky_tracking = [
  ['related-searches', 'Related Searches']
  //...
], elem;

function createClickHandler(name) { // capture only the 'name' variable
  return function (e) {
    e = e || window.event; // cross-browser way to get the event object
    var target = e.target || e.srcElement; // and the event target

    if (target.nodeName == "A") { // make sure that the target is an anchor
      clicky.log(target.href, name, 'outbound');
    }
  };
}

for (var x = 0, len = clicky_tracking.length; x < len; x++) {
  elem = document.getElementById(clicky_tracking[x][0]); // find parent element
  elem.onclick = createClickHandler(clicky_tracking[x][1]); // bind the event
}
​

答案 2 :(得分:1)

授权是一种更好的方法。 JSLint抱怨因为你每次都在循环中创建一个新函数。但是,我宁愿在所有单独的根元素上设置一个处理程序,而不是在文档上设置单个事件来监听所有点击事件。可以为所有这些元素重用单个处理程序。

function logClick(event) {
    event = event || window.event;
    var link = event.target || event.srcElement;

    if(link.nodeName.toLowerCase() !== "a") {
        return;
    }

    var name = clicky_tracking[this.id];
    clicky.log(link.href, name, 'outbound');
}

为每个根元素注册上述处理程序。

for(var id in clicky_tracking) {
    var root = document.getElementById(id);
    root.onclick = logClick;
}

为了避免搜索数组,我已将clicky_tracking从数组更改为对象,以便更方便地进行键控访问。

var clicky_tracking = {
  'related-searches': 'Related Searches',
  'related-stories': 'Related Stories',
  'more-videos': 'More Videos',
  'web-headlines': 'Publication'
};