Как сократить костыль? Как написать короче данный скрипт?

91
03 марта 2022, 08:20

Всем привет! Написал тут решение данной задачи, но мне кажется что это какой-то костыль. Если есть идеи, накидайте, пожалуйста. Дайте аргументы если где-то что-то коряво и или даже если нормально :). Вот сама задача и мое решение:

Дан абзац. Даны чекбоксы 'перечеркнуть', 'сделать жирным', 'сделать красным'. Если соответствующий чекбокс отмечен - заданное действие происходит с абзацем (становится красным, например). Если чекбоксу снять отметку - действие отменяется.

let p = document.querySelector('p'); 
let checkbox = document.querySelectorAll('input'); 
 
for (let i = 0; i < checkbox.length; i++) { 
  if (i === 0) { 
    checkbox[i].addEventListener('click', changeTextDecoration); 
  } 
  if (i === 1) { 
    checkbox[i].addEventListener('click', changeFontWeight); 
  } 
  if (i === 2) { 
    checkbox[i].addEventListener('click', changeColor); 
  } 
} 
 
function changeTextDecoration() { 
  if (this.checked) { 
    p.style.textDecoration = 'line-through'; 
  } else { 
    p.style.textDecoration = 'none'; 
  } 
} 
 
function changeFontWeight() { 
  if (this.checked) { 
    p.style.fontWeight = 'bold'; 
  } else { 
    p.style.fontWeight = 'normal'; 
  } 
} 
 
function changeColor() { 
  if (this.checked) { 
    p.style.color = 'red'; 
  } else { 
    p.style.color = 'black'; 
  } 
}
<p> Paragraph </p> 
<input type="checkbox"> Перечеркнуть 
<input type="checkbox"> Сделать полужирным 
<input type="checkbox"> Сделать красным

Answer 1

Как-нибудь так, например.

let p = document.querySelector('p'); 
let checkbox = document.querySelectorAll('input'); 
     
for ( let i = 0; i < checkbox.length; i++ ){ 
     checkbox[i].addEventListener('click', cbClick);         
} 
 
function cbClick(){         
    const cfg = { 
            strike: { p: 'textDecoration', v: 'line-through', d: 'none' }, 
            bold:   { p: 'fontWeight',     v: 'bold',         d: 'normal'}, 
            red:    { p: 'color',          v: 'red',          d: 'black'}  
          }; 
         
    const x = cfg[this.dataset.type];         
         
    p.style[x.p] = this.checked ? x.v : x.d; 
}; 
     
    
<p> Paragraph </p> 
    <input type="checkbox" data-type="strike"> Перечеркнуть 
    <input type="checkbox" data-type="bold"> Сделать полужирным 
    <input type="checkbox" data-type="red"> Сделать красным

Answer 2

let p = document.querySelector('p'); 
let checkbox = document.querySelectorAll('input'); 
 
checkbox[0].addEventListener('click', changeTextDecoration); 
checkbox[1].addEventListener('click', changeFontWeight); 
checkbox[2].addEventListener('click', changeColor); 
 
function changeTextDecoration() { 
  p.style.textDecoration = this.checked ? 'line-through' : 'none'; 
} 
 
function changeFontWeight() { 
  p.style.fontWeight = this.checked ? 'bold' : 'normal'; 
} 
 
function changeColor() { 
  p.style.color = this.checked ? 'red' : 'black'; 
}
<p> Paragraph </p> 
<input type="checkbox"> Перечеркнуть 
<input type="checkbox"> Сделать полужирным 
<input type="checkbox"> Сделать красным

P.S.Пока писал увидел сообщение от Андрей NOP, это сообщение является его реализацией

Answer 3

или так, например

const p = document.querySelector('p'), 
  checkbox = document.querySelectorAll('input'), 
  changeTextDecoration = e => p.style.textDecoration = e.target.checked ? 'line-through' : 'none', 
  changeFontWeight = e => p.style.fontWeight = e.target.checked ? 'bold' : 'normal', 
  changeColor = e => p.style.color = e.target.checked ? 'red' : 'black' 
 
checkbox[0].addEventListener('click', changeTextDecoration) 
checkbox[1].addEventListener('click', changeFontWeight) 
checkbox[2].addEventListener('click', changeColor)
<p>AHAHAHA</p> 
<input type="checkbox" /> 
<input type="checkbox" /> 
<input type="checkbox" />

Answer 4

Это ваш базовый вариант

let p = document.querySelector('p'); 
let checkbox = document.querySelectorAll('input'); 
 
const listeners = [ 
  changeTextDecoration, 
  changeFontWeight, 
  changeColor 
]; 
for (let i = 0; i < checkbox.length; i++) { 
  checkbox[i].addEventListener('click', listeners[i]); 
} 
 
function changeTextDecoration() { 
  p.style.textDecoration = (this.checked) ? 'line-through' : 'none'; 
} 
 
function changeFontWeight() { 
  p.style.fontWeight = (this.checked) ? 'bold' : 'normal'; 
} 
 
function changeColor() { 
  p.style.color = (this.checked) ? 'red' : 'black'; 
}
<p> Paragraph </p> 
<input type="checkbox"> Перечеркнуть 
<input type="checkbox"> Сделать полужирным 
<input type="checkbox"> Сделать красным

Но идея тупо перебирать в цикле элементы и назначать по порядку обработчики опасна. В середине добавится еще один инпут и все пропало.

Лучше их как-то проидентифицировать

let p = document.querySelector('p'); 
let checkbox = document.querySelectorAll('input'); 
 
const listeners = { 
  decor: changeTextDecoration, 
  weight: changeFontWeight, 
  color: changeColor 
}; 
 
for (let i = 0; i < checkbox.length; i++) { 
  const input = checkbox[i]; 
  input.addEventListener('click', listeners[input.getAttribute('data-id')]); 
} 
 
function changeTextDecoration() { 
  p.style.textDecoration = (this.checked) ? 'line-through' : 'none'; 
} 
 
function changeFontWeight() { 
  p.style.fontWeight = (this.checked) ? 'bold' : 'normal'; 
} 
 
function changeColor() { 
  p.style.color = (this.checked) ? 'red' : 'black'; 
}
<p> Paragraph </p> 
<input type="checkbox" data-id="decor"> Перечеркнуть 
<input type="checkbox" data-id="weight"> Сделать полужирным 
<input type="checkbox" data-id="color"> Сделать красным

Или, учитывая тот факт, что каждый чекбокс включает или выключает какой-то элемент стиля, то можно обойтись одним обработчиком

let p = document.querySelector('p'); 
let checkbox = document.querySelectorAll('input'); 
 
for (let i = 0; i < checkbox.length; i++) { 
  const input = checkbox[i]; 
  input.addEventListener('click', change); 
} 
 
function change() { 
  const attr = (this.checked) ? 'data-checked' : 'data-unchecked'; 
  p.style[this.getAttribute('data-style')] = this.getAttribute(attr) 
}
<p> Paragraph </p> 
<input type="checkbox" data-style="textDecoration" data-checked="line-through" data-unchecked="none"> Перечеркнуть 
<input type="checkbox" data-style="fontWeight" data-checked="bold" data-unchecked="normal"> Сделать полужирным 
<input type="checkbox" data-style="color" data-checked="red" data-unchecked="black"> Сделать красным

Или вынести изменения в отдельные CSS классы и управлять самими классами

let p = document.querySelector('p'); 
let checkbox = document.querySelectorAll('input'); 
 
for (let i = 0; i < checkbox.length; i++) { 
  const input = checkbox[i]; 
  input.addEventListener('click', change); 
} 
 
function change() { 
  const className = this.getAttribute('data-class'); 
  if (this.checked) 
    p.classList.add(className); 
  else 
    p.classList.remove(className); 
}
.decor { 
  text-decoration: line-through; 
} 
 
.weight { 
  font-weight: bold; 
} 
 
.color { 
  color: red; 
}
<p> Paragraph </p> 
<input type="checkbox" data-class="decor"> Перечеркнуть 
<input type="checkbox" data-class="weight"> Сделать полужирным 
<input type="checkbox" data-class="color"> Сделать красным

Answer 5
немного магии и custom elements
  • custom elements
    расширение базовых html элементов, например:

    class XDiv extends HTMLElement {}
    customElements.define('x-div', XDiv)
    
  • ShadowRoot
    грубо говоря - упрощённая версия iframe, например: можно не париться с id

обратите внимание на проброс стилей и чекбоксов в примере

// @ts-check 
class XDiv extends HTMLElement { 
  connectedCallback() { 
    /** @type {ShadowRoot} */ 
    let shadowRoot; 
    try { shadowRoot = this.attachShadow({ mode: 'closed' }) } catch (e) { return; } 
    shadowRoot.appendChild(document.createRange().createContextualFragment(` 
      <slot></slot> 
      <p id="p">Paragraph</p> 
    `)) 
    shadowRoot.addEventListener('change', e => { 
      const { dataset, checked } = e.target; 
      if (!dataset.class) return; 
      const p = shadowRoot.getElementById('p') 
      if (checked) p.classList.add(dataset.class) 
      else p.classList.remove(dataset.class) 
    }) 
    Array.from(this.getElementsByTagName('style'), el => { 
      shadowRoot.appendChild(el) 
    }) 
  } 
} 
customElements.define('x-div', XDiv)
<x-div> 
  <style> 
    .strike { 
      text-decoration: line-through; 
    } 
 
    .bold { 
      font-weight: bold; 
    } 
 
    .red { 
      color: red; 
    } 
  </style> 
  <label><input type="checkbox" data-class="strike" />Перечеркнуть</label> 
  <label><input type="checkbox" data-class="bold" />Сделать полужирным</label> 
  <label><input type="checkbox" data-class="red" />Сделать красным</label> 
</x-div>

Answer 6

<p id="p"> Paragraph </p> 
<input type="checkbox" s="text-decoration=line-through"> Перечеркнуть 
<input type="checkbox" s="font-weight=bold"> Сделать полужирным 
<input type="checkbox" s="color=red"> Сделать красным 
 
<script> 
  Array.prototype.forEach.call( 
    document.querySelectorAll('input'), 
    (i) => i.addEventListener('change', (e) => { 
      let s = e.currentTarget.getAttribute('s').split('=') 
      p.style[s[0]] = e.currentTarget.checked ? s[1] : '' 
    }) 
  ) 
</script>

READ ALSO
Неверные данные в localStorage

Неверные данные в localStorage

У меня есть файл с авторизацией пользователя, при логине я получаю токен в localStorageС этим все в порядке

88
Как сделать мультиязычный лендинг?

Как сделать мультиязычный лендинг?

Подскажите, как реализовать мультиязычный лендинг?

85
Uncaught TypeError: Cannot read property &#39;value&#39; of undefined

Uncaught TypeError: Cannot read property 'value' of undefined

Есть функция, которая записывает полученные с формы html документа значения в переменные а и b, в ней на 47й строке консоль выдает ошибку "Uncaught...

157
WebSoket и Promise || Иммитация Ajax

WebSoket и Promise || Иммитация Ajax

Решил отказаться от Ajax и все взаимодействие с сервером провожу через вебсокеты

81