JS:什么函数会让我的代码变干?
JS: What function will make my code DRY?
我的程序
最初在黑色网格上绘制的 Etch-A-Sketch。用户单击 Erase 为黑色方块着色。用户也可以点击 **Rainbow" 在每个方块上绘制随机颜色,即 square[0] 可以是蓝色,square[1] 可以是紫色......等等,它们在每个 上都是随机的鼠标悬停.
问题
您会注意到 eraseGrid() 和 drawRainbow() 的代码几乎相同。我必须这样做,否则程序将无法正常运行。这是 Rainbow 在每个 mouseover.
上绘制不同颜色的唯一方法
目标
如果你看一下我在底部注释掉的函数,我试着想出一些我可以同时用于 eraseGrid() 和 drawRainbow()[=33= 的东西],但在测试功能时,它没有按预期工作。不是在每个 mouseover 上绘制随机颜色,而是创建随机颜色(假设为蓝色)并在网格上绘制蓝色。如果我重新打开 Rainbow,它会创建另一种随机颜色(例如绿色)并将其绘制在网格上。
我不明白为什么我创建的函数没有按预期工作,而重复的代码却可以。
/**************************** Input->Button DOM ****************************/
const newGrid = document.getElementById('new-grid');
newGrid.addEventListener('click', createGrid);
const erase = document.getElementById('erase');
erase.addEventListener('click', eraseGrid);
const rainbow = document.getElementById('rainbow');
rainbow.addEventListener('click', drawRainbow);
/*********************** Grid variable and creation ***********************/
const main = document.querySelector('main');
const div = main.getElementsByTagName('div');
drawGrid(16, ((600 / 16) - 2) + 'px');
pickColor('#333');
function createGrid() {
// removes divs from largest to smallest
for (let i = main.childNodes.length - 1; i >= 0 ; i--) {
main.removeChild(main.childNodes[i]);
}
let size;
do {
size = parseInt(prompt("Please enter a number from 1 to 64", ""), 10);
} while(Number.isNaN(size) || size > 64 || size < 1);
const numPx = (600 / size) - 2;
let px = numPx + 'px';
drawGrid(size, px);
pickColor('#333');
}
function pickColor(color) {
for (let i = 0; i < main.childNodes.length; i++) {
main.childNodes[i].addEventListener('mouseover', function change() {
main.childNodes[i].style.backgroundColor = color;
})
}
}
// draw grid of div elements
function drawGrid(size, px) {
for (let i = 0; i < size; i++) {
for (let j = 0; j < size; j++) {
const div = document.createElement('div');
main.appendChild(div);
div.setAttribute('style', `width: ${px}; height: ${px};
float: left; border: 1px solid #333;`);
}
}
// clear floats
const div = document.createElement('div');
div.setAttribute('class', 'clear');
main.appendChild(div);
}
function eraseGrid() {
erase.classList.toggle('erase');
if (erase.className === 'erase') {
rainbow.classList.remove('rainbow');
main.addEventListener('mouseover', function(){
pickColor('#f2f2f2');
})
}
else {
main.addEventListener('mouseover', function(){
pickColor('#333');
})
}
}
function randColor() {
let arr = [];
for (let i = 0; i < 3; i++) {
arr.push(Math.floor(Math.random() * 255));
}
return arr;
}
function drawRainbow() {
rainbow.classList.toggle('rainbow');
if (rainbow.className === 'rainbow') {
erase.classList.remove('erase');
main.addEventListener('mouseover', function(){
pickColor('rgb(' + randColor() + ')');
})
}
else {
main.addEventListener('mouseover', function(){
pickColor('#333');
})
}
// changeColor(rainbow, 'rainbow', erase, 'erase', 'rgb(' + randColor() + ')')
}
/*function changeColor(newClass, newClassStr, oldClass, oldClassStr, color) {
newClass.classList.toggle(newClassStr);
if (newClass.className === newClassStr) {
oldClass.classList.remove(oldClassStr);
main.addEventListener('mouseover', function(){
pickColor(color);
})
}
else {
main.addEventListener('mouseover', function(){
pickColor('#333');
})
}
}*/
你可以算出名字,但看起来你只是在做
function go(str1, str2){
document.getElementById(str1).classList.toggle(str1);
if (document.getElementById(str1).className === str1) {
document.getElementById(str2).classList.remove(str2);
main.addEventListener('mouseover', function(){
pickColor('#f2f2f2');
})
}
else {
main.addEventListener('mouseover', function(){
pickColor('#333');
})
}
}
只需用 go('erase', 'rainbow')
或 go('rainbow', 'erase')
调用即可
function eraseGrid(){
go('erase', 'rainbow');
}
function drawRainbow(){
go('rainbow', 'erase');
}
至于被注释掉的 changeColor 函数,你应该可以像这样重构它,如果你把 rainbow 和 erase 放在父对象中,这样你就可以通过名称找到它们。
const objects = {erase, rainbox};
function changeColor(str1, str2) {
objects[str1].classList.toggle(str1);
if (objects[str1].className === str1) {
objecs[str2].classList.remove(str2);
main.addEventListener('mouseover', function(){
pickColor('rgb(' + randColor() + ')');
})
}
else {
main.addEventListener('mouseover', function(){
pickColor('#333');
})
}
}
同样,正如@me_ 在评论中提到的那样。在每次点击时添加一个 eventListener 很可能不是您想要的。第五次点击后,您将拥有五个事件监听器和五个函数 function(){pickColor('rgb(' + randColor() + ')');}
,每次您将鼠标悬停在 "main" 上时都会调用它们(每次点击都会扩展...)
(编辑,因为评论太严格了:)
00Saad: I saw there's a removeEventListener() method, would I implement it by adding main.removeEventListener('mouseover', function(){ pickColor('rgb(' + randColor() + ')'); }) after the addEventListener?
不,如果您希望以后能够轻松删除它,请将函数存储在变量中。
function(){...}
<-- 这是创建新函数的代码段。
所以这个:main.removeEventListener('mouseover', function(){...})
,将创建一个新的匿名函数,并尝试从 main 的事件侦听器中删除(它不存在的地方)。
此外,如果您使用存储函数 (const a = function(){..}
) 和后来的 addEventListener('mouseover', a)
) 将 kinda 解决问题,因为您不能绑定相同的无论如何多次对同一事件起作用(它不会有任何效果)。
但是接下来的 addEventListener
调用不会有任何区别,因此在您的情况下,将鼠标悬停侦听器放在 changeColor()
.[=20= 的范围之外会更有意义]
let isRandomColor = true;
main.addEventListener('mouseover', function(){
pickColor( isRandomColor ? randomColor() : '#333' );
});
function changeColor(){
if (...){
isRandomColor = true;
} else {
isRandomColor = false;
}
}
我的程序
最初在黑色网格上绘制的 Etch-A-Sketch。用户单击 Erase 为黑色方块着色。用户也可以点击 **Rainbow" 在每个方块上绘制随机颜色,即 square[0] 可以是蓝色,square[1] 可以是紫色......等等,它们在每个 上都是随机的鼠标悬停.
问题 您会注意到 eraseGrid() 和 drawRainbow() 的代码几乎相同。我必须这样做,否则程序将无法正常运行。这是 Rainbow 在每个 mouseover.
上绘制不同颜色的唯一方法目标
如果你看一下我在底部注释掉的函数,我试着想出一些我可以同时用于 eraseGrid() 和 drawRainbow()[=33= 的东西],但在测试功能时,它没有按预期工作。不是在每个 mouseover 上绘制随机颜色,而是创建随机颜色(假设为蓝色)并在网格上绘制蓝色。如果我重新打开 Rainbow,它会创建另一种随机颜色(例如绿色)并将其绘制在网格上。
我不明白为什么我创建的函数没有按预期工作,而重复的代码却可以。
/**************************** Input->Button DOM ****************************/
const newGrid = document.getElementById('new-grid');
newGrid.addEventListener('click', createGrid);
const erase = document.getElementById('erase');
erase.addEventListener('click', eraseGrid);
const rainbow = document.getElementById('rainbow');
rainbow.addEventListener('click', drawRainbow);
/*********************** Grid variable and creation ***********************/
const main = document.querySelector('main');
const div = main.getElementsByTagName('div');
drawGrid(16, ((600 / 16) - 2) + 'px');
pickColor('#333');
function createGrid() {
// removes divs from largest to smallest
for (let i = main.childNodes.length - 1; i >= 0 ; i--) {
main.removeChild(main.childNodes[i]);
}
let size;
do {
size = parseInt(prompt("Please enter a number from 1 to 64", ""), 10);
} while(Number.isNaN(size) || size > 64 || size < 1);
const numPx = (600 / size) - 2;
let px = numPx + 'px';
drawGrid(size, px);
pickColor('#333');
}
function pickColor(color) {
for (let i = 0; i < main.childNodes.length; i++) {
main.childNodes[i].addEventListener('mouseover', function change() {
main.childNodes[i].style.backgroundColor = color;
})
}
}
// draw grid of div elements
function drawGrid(size, px) {
for (let i = 0; i < size; i++) {
for (let j = 0; j < size; j++) {
const div = document.createElement('div');
main.appendChild(div);
div.setAttribute('style', `width: ${px}; height: ${px};
float: left; border: 1px solid #333;`);
}
}
// clear floats
const div = document.createElement('div');
div.setAttribute('class', 'clear');
main.appendChild(div);
}
function eraseGrid() {
erase.classList.toggle('erase');
if (erase.className === 'erase') {
rainbow.classList.remove('rainbow');
main.addEventListener('mouseover', function(){
pickColor('#f2f2f2');
})
}
else {
main.addEventListener('mouseover', function(){
pickColor('#333');
})
}
}
function randColor() {
let arr = [];
for (let i = 0; i < 3; i++) {
arr.push(Math.floor(Math.random() * 255));
}
return arr;
}
function drawRainbow() {
rainbow.classList.toggle('rainbow');
if (rainbow.className === 'rainbow') {
erase.classList.remove('erase');
main.addEventListener('mouseover', function(){
pickColor('rgb(' + randColor() + ')');
})
}
else {
main.addEventListener('mouseover', function(){
pickColor('#333');
})
}
// changeColor(rainbow, 'rainbow', erase, 'erase', 'rgb(' + randColor() + ')')
}
/*function changeColor(newClass, newClassStr, oldClass, oldClassStr, color) {
newClass.classList.toggle(newClassStr);
if (newClass.className === newClassStr) {
oldClass.classList.remove(oldClassStr);
main.addEventListener('mouseover', function(){
pickColor(color);
})
}
else {
main.addEventListener('mouseover', function(){
pickColor('#333');
})
}
}*/
你可以算出名字,但看起来你只是在做
function go(str1, str2){
document.getElementById(str1).classList.toggle(str1);
if (document.getElementById(str1).className === str1) {
document.getElementById(str2).classList.remove(str2);
main.addEventListener('mouseover', function(){
pickColor('#f2f2f2');
})
}
else {
main.addEventListener('mouseover', function(){
pickColor('#333');
})
}
}
只需用 go('erase', 'rainbow')
或 go('rainbow', 'erase')
function eraseGrid(){
go('erase', 'rainbow');
}
function drawRainbow(){
go('rainbow', 'erase');
}
至于被注释掉的 changeColor 函数,你应该可以像这样重构它,如果你把 rainbow 和 erase 放在父对象中,这样你就可以通过名称找到它们。
const objects = {erase, rainbox};
function changeColor(str1, str2) {
objects[str1].classList.toggle(str1);
if (objects[str1].className === str1) {
objecs[str2].classList.remove(str2);
main.addEventListener('mouseover', function(){
pickColor('rgb(' + randColor() + ')');
})
}
else {
main.addEventListener('mouseover', function(){
pickColor('#333');
})
}
}
同样,正如@me_ 在评论中提到的那样。在每次点击时添加一个 eventListener 很可能不是您想要的。第五次点击后,您将拥有五个事件监听器和五个函数 function(){pickColor('rgb(' + randColor() + ')');}
,每次您将鼠标悬停在 "main" 上时都会调用它们(每次点击都会扩展...)
(编辑,因为评论太严格了:)
00Saad: I saw there's a removeEventListener() method, would I implement it by adding main.removeEventListener('mouseover', function(){ pickColor('rgb(' + randColor() + ')'); }) after the addEventListener?
不,如果您希望以后能够轻松删除它,请将函数存储在变量中。
function(){...}
<-- 这是创建新函数的代码段。
所以这个:main.removeEventListener('mouseover', function(){...})
,将创建一个新的匿名函数,并尝试从 main 的事件侦听器中删除(它不存在的地方)。
此外,如果您使用存储函数 (const a = function(){..}
) 和后来的 addEventListener('mouseover', a)
) 将 kinda 解决问题,因为您不能绑定相同的无论如何多次对同一事件起作用(它不会有任何效果)。
但是接下来的 addEventListener
调用不会有任何区别,因此在您的情况下,将鼠标悬停侦听器放在 changeColor()
.[=20= 的范围之外会更有意义]
let isRandomColor = true;
main.addEventListener('mouseover', function(){
pickColor( isRandomColor ? randomColor() : '#333' );
});
function changeColor(){
if (...){
isRandomColor = true;
} else {
isRandomColor = false;
}
}