链接功能在内部其他功能时不起作用

时间:2017-07-30 08:15:31

标签: javascript

我尝试使用vanilla javascript创建链接函数,如果只是链接它的工作,但如果在其他函数内它停止工作。

var doc = document,
M$ = function(el) {
  var expr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/;
  var m = expr.exec(el);
  if(m[1]) {
    return doc.getElementById(m[1]);
  } else if(m[2]) {
    return doc.getElementsByTagName(m[2]);
  } else if(m[3]) {
    return doc.getElementsByClassName(m[3]);
  }
},
$ = function (el) {
  this.el = M$(el);

  // event function
  this.event = function(type,fn) {
    this.el.addEventListener(type,fn,false);
    return this;
  }

  // forEach function
  this.forEach = function(fn,val) {
    for(var i = this.el.length - 1; i >= 0; i--) {
      fn.call(val, i, this.el[i]);
    }
    return this;
  }

  if(this instanceof $) {
    return this.$;
  } else {
    return new $(el);
  }
};

//use
$("button").forEach(function(index, el) 
 // when i use function event, its not work
  el.event("click", function() {
    alert("hello");
  });
  // if i'm using addEventListener its work, but i want use event function
});

我的问题是,如何在forEach函数中使用事件函数? 谢谢你的帮助!

3 个答案:

答案 0 :(得分:1)

首先,您遗失$("button").forEach(function(index, el)之后代码中的括号会出现问题{;

然后问题是,当你尝试在元素(按钮)上调用click-callback时,事实上,由于this问题,元素(按钮)没有event()属性。自this.el = M$(el);走出forEach()以来,它们甚至都没有定义。我调整并清理了一些你的代码,检查出来。我想现在它可以做你想要的:

var doc = document,
M$ = function(el) { 
  var expr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/;
  var m = expr.exec(el);
  if(m[1]) return doc.getElementById(m[1]); else if(m[2]) return doc.getElementsByTagName(m[2]); else if(m[3]) return doc.getElementsByClassName(m[3]);
}, 
$ = function(el) { 
  this.forEach = function(fn,val) {  
  // assign this.el and this.el[i].event inside forEach(), not outside  
    this.el = M$(el);
    for(var i = this.el.length - 1; i >= 0; i--) {
        this.el[i].event = function(type,fn) { this.addEventListener(type,fn,false); };
        fn.call(val, i, this.el[i]);
    }
  }
  return this;
};
$("button").forEach(function(index, el) {
    el.event("click", function() { alert("hello, " + this.textContent); });
});
<button>btn1</button>
<button>btn2</button>

<强>更新

虽然之前的解决方案适用于在按钮上设置点击处理程序的特定目的,但我认为您真正想要的是模拟Jquery和链函数调用。我以这种方式改进了你的尝试:

var doc = document,
M$ = function(el) {
  var expr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/;
  var m = expr.exec(el);
  if(m[1]) return doc.getElementById(m[1]);else if(m[2]) return doc.getElementsByTagName(m[2]); else if(m[3]) return doc.getElementsByClassName(m[3]);
},
$ = function (el) { //console.log(this);
  this.el = M$(el);
  this.event = function(type,fn) {
    for(var i = this.el.length - 1; i >= 0; i--) this.el[i].addEventListener(type,fn,false);
  }
  this.forEach = function(fn) { 
      fn.call(this);
  }
  return this;
};

$("button").forEach(function() {
  this.event("click", function() {
    alert("hello, " + this.textContent);
  });
});
<button>btn1</button>
<button>btn2</button>

理解这一点的关键是您的this对象应始终等于$ {el: HTMLCollection(2), event: function, forEach: function}。所以,

  • 使用HTML Collection和event&amp; forEach函数调用$("button")您最初将其设置为$ {el: HTMLCollection(2), event: function, forEach: function};
  • 调用$("button").forEach(fn),使forEach的上下文等于上一步中的this;
  • fn.call(this);内拨打forEach()您拨打回拨fn并将相同的this传递给它;
  • 在您致电fn的回调this.event()中 - 它有效,因为您的this始终是第一步中的那个。
  • this.event()只是$.event()我们只是遍历我们的HTMLCollection并为按钮上的click事件设置处理程序。内部$.event() this将等同于一个按钮元素,因为我们在点击事件的上下文中调用它,因此,this.textContent会获取按钮的内容。

谢谢,非常好的问题!

答案 1 :(得分:0)

首先要做的事情。

1

this.el = M$(el); 

M$ = function(el) {
  var expr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/;
  var m = expr.exec(el);
  if(m[1]) {
    return doc.getElementById(m[1]);
  } else if(m[2]) {
    return doc.getElementsByTagName(m[2]);
  } else if(m[3]) {
    return doc.getElementsByClassName(m[3]);
  }
}

当您定义M $时,如果按标签名称或类名获取元素,则可以使用HtmlCollection,如果按ID获取元素,则只需要一个元素。

然后你假设你的el可以是一个集合。

this.event = function(type,fn) {
    this.el.addEventListener(type,fn,false);
    return this;
  }

如果您尝试获取所有按钮,则可能会收到一个集合。

2

如果您尝试运行已发布的代码,则会收到意外的标识符错误,因为您错过了{forEach(function(index, el)之后。

3

如果你把那个{放在那里,你会收到一个el.event并不是一个功能错误,因为你没有el上的事件功能,但你有{{1} }}。

4

如果您将代码更改为:

$(el)

您将收到错误,因为您没有处理多个元素。见1个问题。

答案 2 :(得分:0)

看看这个。

var doc = document,
M$ = function(el) {
  var expr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/;
  var m = expr.exec(el);
  if(m[1]) {
    return Array.apply([],[doc.getElementById(m[1])]);
  } else if(m[2]) {
    return Array.apply([],doc.getElementsByTagName(m[2]));
  } else if(m[3]) {
    return Array.apply([],doc.getElementsByClassName(m[3]));
  }
},
$ = function (el) {
  if(! (this instanceof $)) {
    return new $(el);
  }
  this.els = M$(el);

  // event function
  this.event = function(type,fn) {
    this.forEach(function(index, el){
      el.addEventListener(type,fn,false);
    });
    return this;
  }

  // forEach function
  this.forEach = function(fn,val) {
    for(var i = this.els.length - 1; i >= 0; i--) {
      fn.call(val, i, this.els[i]);
    }
    return this;
  }
  return this;
};

//use
$("button").event("click", function() {
    alert("hello");
});
  • 这里使M$ function返回一个数组以保持一致。
  • 因此,$().event函数已更改为迭代this.els中的所有元素。
  • 因此,您只需调用$("button").event函数而不是$("button").forEach函数来注册事件侦听器。

参考:Demo 这个有效。但是,这是你想要的吗?我不确定。