JavaScript动态按钮onClick事件(变量范围)

时间:2018-03-01 21:56:30

标签: javascript-events

背景

我根据查询数据库的AJAX请求返回的JSON对象动态创建了很多按钮。

然后每个按钮需要对相同的数据库进行后续的AJAX调用,具体取决于我放入每个按钮的onClick函数的信息。

基本上,网址为"api/",如果我只调用foo,则会为"api/book/"提供所有可能的选项。如果我致电bar,它会为foo = "book" //getJSON is a promise function that returns a JSON object getJSON("api") // volumes is the JSON object that contains an array of objects // each object is {book:"someTitle",url:"titleShorthand"} .then((volumes) => { for (var vol in volumes) { //create Button returns a document.crateElement(input) //with type=button and value = the passed in param. var btn = createButton(volumes[vol].book) btn.addEventListener('click', function(){ getJSON("api/" + volumes[vol].url) .then((books) => { console.log(books) }) }) volumeDiv.appendChild(btn) } }) 提供所有可能的选项,等等......

我正在尝试的方法。

console.log(books)

问题

所有的button.onClick事件都是一样的,因为volumes[vol].url只给我最后一套书,无论我点击哪个按钮。对于每个按钮而言,{{1}}似乎都会被for循环的每次迭代重写,而不仅仅是新创建的按钮。

1 个答案:

答案 0 :(得分:0)

那是因为您在for循环内声明了一个函数。由于closures的工作原理,volume[vol]的值始终是您在循环中声明的函数运行时的最后一个值。

要解决此问题,您可以使用higher order function

我不得不模拟一些函数来使这个代码段工作,但是您必须在代码中编写类似getEventListener的函数才能使其正常工作。



var volumeDiv = document.getElementById('container')

function getJSON (url) {
  return new Promise (resolve => {
    var vol1 = {
      book: "vol1",
      url: "vol1"
    }
    var vol2 = {
      book: "vol2",
      url: "vol2"
    }
    var map = {
      'api': { vol1, vol2 },
      'api/vol1': vol1,
      'api/vol2': vol2
    }
    resolve(map[url])
  })
}

function createButton (name) {
  console.log(`creating button ${name}`)
  var btn = document.createElement('input')
  btn.type = 'button'
  btn.value = name

  return btn
}

function getEventListener (url) {
  return () => {
    getJSON(url)
    .then((books) => {
      console.log(books)
    })
  }
}

getJSON("api")
  .then(volumes => {
    for (var vol in volumes) {
      var volumeUrl = volumes[vol].url
      var btn = createButton(volumes[vol].book)
      btn.addEventListener('click', getEventListener("api/" + volumeUrl))
      volumeDiv.appendChild(btn)
    }
  })

<div id="container" />
&#13;
&#13;
&#13;