JS. Как избавиться от повторяющихся строк кода?

Всем привет ;)

Написал код на JS, но меня очень смущает, что он повторяется, хочется его минимизировать.

mobileMenuButton.addEventListener('click', () => {
    
    if (cloneMenu.style.visibility === 'hidden') {
        cloneMenu.style.visibility = 'visible';
        cloneMenu.style.opacity = '100%';
    } else {
        cloneMenu.style.visibility = 'hidden';
        cloneMenu.style.opacity = '0%';
    }
});

header.addEventListener('click', (e) => {
    if (e.target === body)
    cloneMenu.style.visibility = 'hidden';
        cloneMenu.style.opacity = '0%';
});

body.addEventListener('keydown', e => {
    if (e.code === "Escape") {
        cloneMenu.style.visibility = 'hidden';
        cloneMenu.style.opacity = '0%';
    }
});


const close = () => {
    sF.style.visibility = 'hidden';
    sF.style.opacity = '0%';
};

body.addEventListener('click', (e) => {
    if (e.target === body)
        return close();
});
sF.addEventListener('keydown', e => {
    if (e.code === "Escape") {
        return close();
    }
});

search.addEventListener('click', () => {
    if (sF.style.visibility === 'hidden') {
        sF.style.visibility = 'visible';
        sF.style.opacity = '100%';
        setTimeout(() => {
            return input.focus();
        }, 100);

    } else {
        return close();
    }
});

Прикрепил код, объявление и присвоение переменных есть, но добавлять сюда не стал, т.к. особо это ситуацию не поменяет.

Подскажите, люди добрые, по какому принципу можно этот код уменьшить, привести к каким-то общим конструкциям, которые решали бы задачу нескольких элементов?


Ответы (1 шт):

Автор решения: Егор Банин

Очень полезный и важный инструмент программиста -- абстракция. Постарайтесь увидеть общее в коде, который пишите.

Например вам удалось заметить, что строчки

sF.style.visibility = 'hidden';
sF.style.opacity = '0%';

не отдельные и независимые, а представляют из себя функцию close(). И это сократило ваш код, не так ли?

Теперь интересный момент. Строки

cloneMenu.style.visibility = 'hidden';
cloneMenu.style.opacity = '0%';

это тоже close(). Но другого элемента. А вы наверняка знаете, что функции могут принимать аргументы и менять своё поведение в зависимости от этих аргументов.

const close = el => {
    el.style.visibility = 'hidden';
    el.style.opacity = '0%';
};

Теперь можно закрывать что угодно: close(sF), close(cloneMenu)

Вы можете продолжать это упражнение и заметить, что строки

sF.style.visibility = 'visible';
sF.style.opacity = '100%';

тоже связаны, и это можно было бы назвать open() и даже open(sF).

И даже это не предел абстрагирования! На следующем уровне вы можете заметить, что open и close объединяет то, что они относятся к элементу, объекту вашего интерфейса -- виджету, управляют открытостью и закрытостью виджета. Это ООП.

class Widget {
  
  constructor(el) {
    this.el = el;
  }
  
  open() {
    this.el.style.visibility = 'visible';
    this.el.style.opacity = '100%';
  }
  
  close() {
    this.el.style.visibility = 'hidden';
    this.el.style.opacity = '0%';
  }
  
  isClosed() {
    return this.el.style.visibility === 'hidden';
  }
  
}

После этого ваш код будет приблизительно такой:

mobileMenuButton.addEventListener('click', () => {
    
    if (cloneMenu.isClosed()) {
        cloneMenu.open();
    } else {
        cloneMenu.close();
    }
});

header.addEventListener('click', (e) => {
    if (e.target === body) {
        cloneMenu.close();
    }
});

body.addEventListener('keydown', e => {
    if (e.code === "Escape") {
        cloneMenu.close();
    }
});

body.addEventListener('click', (e) => {
    if (e.target === body) {
        sF.close();
    }
});
sF.addEventListener('keydown', e => {
    if (e.code === "Escape") {
        sF.close();
    }
});

search.addEventListener('click', () => {
    if (sF.isClosed()) {
        sF.open();
        setTimeout(() => {
            return input.focus();
        }, 100);

    } else {
        sF.close();
    }
});

Потом вы можете заметить, что открытие или закрытие виджета, в зависимости от того закрыт ли он или открыт, можно назвать toggle() и снова убрать немного дублирования.

Обработку кликов и нажатий клавиш тоже можно абстрагировать. Но постарайтесь не переусердствовать. Важно вовремя остановится :-)

И прочитайте книгу, которую вам советуют.

→ Ссылка