Ошибка из-за того, что элемент пока не существует

У меня на странице выводится таблица. Если в ней больше 3 строк, то средствами PHP в HTML также выводится кнопка "Показать ещё". Проблема в том, что сейчас у меня всего 2 строки, и поэтому в браузере я получаю ошибку: Uncaught TypeError: document.querySelector(...) is null

Урезанный код кнопки:

document.querySelector('#showmore').onclick = function() { 
    ...
}

И как мне быть, в случае, если я любитель делать всё в лучшем виде? Только через оборачивание?:

if (document.querySelector('#showmore') !== null) {
    document.querySelector('#showmore').onclick = function() { 
    ...
}

Может есть способ получше? (без обходных путей по типу: "да просто выводи кнопку скрытой при любом количестве строк").


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

Автор решения: Quazimorda

В порядке бреда так:

let button = document.querySelector('#showmore');
if (button)
{
  button.addEventListener('click', function(){
    ...
  });
}
→ Ссылка
Автор решения: Pavel Grishaev

Если есть препроцессор или новейшие браузеры только, то можно так:

document.querySelector('#showmore')?.addEventListener( 'click', ()=>{
   // ...
});
→ Ссылка
Автор решения: OPTIMUS PRIME

Пара извращенных функций для разнообразия)
(Конечно, лучше использовать более простые решения, но кому-то было скучно).

1: Найти и вернуть элемент, если он есть, или создать (затратная операция!) и подсунуть div, чтобы избежать ошибки. Код применится к созданному div, который вскоре удалится из-за отсутствия ссылок на него.

safe("#moo").addEventListener("click", Function);
safe("#foo").textContent = "ok";

function safe(selector) {
  return document.querySelector(selector) || document.createElement("div");
}
<div id="foo"></div>

2: Разместить код, зависимый от селектора, в функцию:

execFor("#foo", function() {
  // Весь ваш код, который должен запускаться только при наличии элемента.
  // this - найденный элемент.
  
  this.addEventListener("click", function() {
    console.log("Ура!");
  });
});

execFor("#moo", () => console.log("Не вызывается"));

/***/
function execFor(selector, fn) {
  let node = document.querySelector(selector);
  
  if (node && fn) fn.call(node);
  // Функция вызывается в контексте найденного элемента,
  // Но только если он существует.
  
  return node;
}
<button id="foo"> Click </div>

→ Ссылка
Автор решения: Ruslan Mart

Самый нормальный способ - это проверить явно:

const showMoreNode = document.querySelector('#showmore');

if (showMoreNode !== null) {
    showMoreNode.onclick = function() { 
    ...
}

Можно ещё сделать через делегирование событий:

document.addEventListener('click', ({ target }) => {
  if (target.matches('#showmore, #showmore *')) {
    console.log('hello');
  }
});

Или вообще через атрибут:

<button id="showmore" onclick="showMore()">Show more</button>
function showMore() {

}
→ Ссылка
Автор решения: Михаил Тестов

Я, как автор вопроса, хочу спросить, как вам такое моё решение? :

const buttonShowMore = document.querySelector('#showmore') ? document.querySelector('#showmore') : new Object();
buttonShowMore('#showmore').onclick = function() { 
  ...
}

Или можно ещё короче:

(document.querySelector('#showmore') ? document.querySelector('#showmore') : {}).onclick = function() {
  ...
}

Или совсем коротко:

(document.querySelector('#showmore') || {}).onclick = function() {
  ...
}
→ Ссылка