在另一个eventListener的函数中使用eventListener是不好的做法?

问题描述 投票:0回答:3

我的代码按预期运行,我正在从数组创建一个下拉菜单。然后,我将获取每个下拉列表的值,并根据下拉列表的选择将变量分配给一个数字。然后通过innerHTML显示变量得到p>。 (我尝试使用appendChild(document.createTextNode)而不是innerHTML,但它会继续将结果添加到p>。

我需要它根据下拉列表更改(不添加)。此外,我需要将下拉列表的变量结果(Number)和event.target.value信息传递给输入字段上的另一个eventlistener,该字段接受用户输入并将该输入与第一个eventlistener中的变量相乘。然后将内部HTML注入第二个<p>

我得到了这个工作,但问题是将eventlistener放在另一个eventlistener中是不是这种做法?还有其他解决方案吗?我尝试拔出第一个回调函数并使用返回的变量创建自己的函数。我把它叫做第二个eventlistener,但是项目最终都是未定义的(特别是event.target.value)。

这是我的代码库:

HTML

<select id='cameraMakes' class='cameraSelects'></select>
     <p id='yourCrop'></p>
        <input id="length" type="text" name="lens" placeholder="Enter lens mm a" /><br>
        <p id='results'></p>

JS

const cameraMakeArray = ['Canon5DM2', 'PanasonicGH5', 'SonyA7CropMode']
const cameraMake = document.getElementById("cameraMakes")
const length = document.getElementById("length") 
const yourCrop = document.querySelector("#yourCrop")
const results = document.querySelector("#results")

cameraMakeArray.forEach(camera => {
    let opt = document.createElement('option');
    opt.innerHTML = camera;
    opt.value = camera;
    document.createElement
    cameraMake.appendChild(opt);
    })

cameraMake.addEventListener('change', (event) => {
    let crop = 0
    if (event) {
        results.innerHTML = '';
        length.value = ''
    }
    if (event.target.value === 'Canon5DM2') {
        crop = 1;
    } else if (event.target.value === 'PanasonicGH5') {
        crop = 2;
    } else if (event.target.value === 'SonyA7CropMode') {
        crop = 1.5
    }
    yourCrop.innerHTML = `The ${event.target.value} has a ${crop}x factor`;
    length.addEventListener('input', () => {
        if (length.value) {
        results.innerHTML = `A ${length.value}mm lens is equivalent to a ${length.value * crop}mm lens on the ${event.target.value}`
        } else {
            results.innerHTML = ''
        }
    })
})
javascript html
3个回答
1
投票

这取决于每个用例。但是,在你的情况下,它是过度的,因为每次更改select时你并不真的想要一个全新的函数,你只想改变输出。如果你只是扩大了crop的范围,你可以只设置一次输入处理程序。

此外,当涉及的字符串不包含任何HTML时,您不应使用.innerHTML,因为.innerHTML具有性能和安全隐患。请改用.textContent

const cameraMakeArray = ['Canon5DM2', 'PanasonicGH5', 'SonyA7CropMode']
const cameraMake = document.getElementById("cameraMakes")
const length = document.getElementById("length") 
const yourCrop = document.querySelector("#yourCrop")
const results = document.querySelector("#results")

// With the output variable stored at a higher scope than
// either callback function, one function can set it and the
// other can use it. This allows you to get rid of the nested
// event handler.
let crop = 0

cameraMakeArray.forEach(camera => {
    let opt = document.createElement('option');
    opt.textContent = camera;
    opt.value = camera;
    document.createElement
    cameraMake.appendChild(opt);
});

cameraMake.addEventListener('change', (event) => {
    if (event) {
        results.textContent = '';
        length.value = ''
    }
    if (event.target.value === 'Canon5DM2') {
        crop = 1;
    } else if (event.target.value === 'PanasonicGH5') {
        crop = 2;
    } else if (event.target.value === 'SonyA7CropMode') {
        crop = 1.5
    }
    yourCrop.textContent = `The ${event.target.value} has a ${crop}x factor`;
});

length.addEventListener('input', (evt) => {
        if (length.value) {
        results.textContent = `A ${length.value}mm lens is equivalent to a ${length.value * crop}mm lens on the ${cameraMake.options[cameraMake.selectedIndex].textContent}`
        } else {
            results.textContent = '';
        }
});
<select id='cameraMakes' class='cameraSelects'></select>
<p id='yourCrop'></p>
<input id="length" type="text" name="lens" placeholder="Enter lens mm a" /><br>
<p id='results'></p>

您可能还需要考虑将select数组值更改为对象。这样你可以存储键和值,你不必做任何if/then来根据选择设置变量。此外,如果将第二个回调分离为命名函数,则可以在选择更改时调用它以在输出区域中立即更新。

// Now, each camera can store a key along with a value:
const cameras = {
  Canon5DM2: 1,
  PanasonicGH5: 2,
  SonyA7CropMode: 1.5
};

const cameraMakes = document.getElementById("cameraMakes")
const length = document.getElementById("length") 
const yourCrop = document.querySelector("#yourCrop")
const results = document.querySelector("#results")

// Loop through the object:
for(camera in cameras){
    let opt = document.createElement('option');
    opt.textContent = camera;
    opt.value = cameras[camera]; // Get the value that goes with the key
    cameraMakes.appendChild(opt);
}

cameraMakes.addEventListener('change', (event) => {
  yourCrop.textContent = 
    `The ${cameraMakes.options[cameraMakes.selectedIndex].textContent} has a ${cameraMakes.value}x factor`;
 if(results.textContent !== ""){
   displayResults(); // Update the output because the camera changed
 }
});

// By making this a function declaration, you can call it manually
function displayResults() {
  results.textContent = 
    `A ${length.value}mm lens is equivalent to a ${length.value * cameraMakes.value}mm lens on the ${cameraMakes.options[cameraMakes.selectedIndex].textContent}`;
}

length.addEventListener('input', displayResults);
<select id='cameraMakes' class='cameraSelects'></select>
<p id='yourCrop'></p>
<input id="length" type="text" name="lens" placeholder="Enter lens mm a" /><br>
<p id='results'></p>

0
投票

看看这个版本

const cameraMakeObject = {
  'Canon5DM2': 1,
  'PanasonicGH5': 2,
  'SonyA7CropMode': 1.5
}
const cameraMake = document.getElementById("cameraMakes")
const length = document.getElementById("length")
const yourCrop = document.querySelector("#yourCrop")
const results = document.querySelector("#results")
let crop = 0

const calc = function() {
  results.textContent = (length.value) ? 
     `A ${length.value}mm lens is equivalent to a ${length.value * cameraMakeObject[cameraMake.value]}mm lens on the ${cameraMake.value}` : '';
};

Object.keys(cameraMakeObject).forEach(camera => {
  let opt = document.createElement('option');
  opt.innerHTML = camera;
  opt.value = camera;
  cameraMake.appendChild(opt);
})

cameraMake.addEventListener('change', function() {
  let val = cameraMakeObject[this.value] || "unknown"
  yourCrop.innerHTML = val === "unknown" ? "" : `The ${this.value} has a ${val}x factor`;
})

length.addEventListener('input', calc)
<select id='cameraMakes' class='cameraSelects'>
<option value="">Please select</option>
</select>
<p id='yourCrop'></p>
<input id="length" type="text" name="lens" placeholder="Enter lens mm a" /><br>
<p id='results'></p>

0
投票

It's not bad practice, but it's incorrect here

在您的用例中,每次将更改应用于#cameraMake时,您都在创建一个新的事件侦听器,因此,在五次更改后,#length上的输入事件将有五个事件侦听器。

您要做的是创建一次事件侦听器,因此从更改事件侦听器中删除它是正确的用例。

// create crop here so you can access it in either listener
let crop = 0;

cameraMake.addEventListener('change', event => {
  // modify crop
  crop = /* a value */
})

// attached only once
length.addEventListener('input', () => {
  // instead of using `event.target.value` use `cameraMake.value`
  // you can now access crop here
})

When to nest event listeners

将事件侦听器放置在另一个事件侦听器中的正确用例可能是创建一个事件侦听器,该侦听器仅在外部侦听器触发后触发一次。

注意:还有其他用例,请确保在嵌套事件侦听器时检查代码的工作方式,并删除额外/陈旧事件侦听器。

例:

function listener(event) {
  // defined here so it can be removed
}
el1.addEventListener('some-event', event => {
  el2.removeEventListener('some-event', listener)
  el2.addEventListener('some-event', listener, { once: true })
})
© www.soinside.com 2019 - 2024. All rights reserved.