На что поменять querySelector и можно ли ввести data-action

let a = "";
let b = "";
let sign = "";
let finish = false;

const digit = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."];
const action = ["-", "+", "X", "÷",];

const out = document.querySelector(".screen p[data-output]");

function clearAll() {
  a = "";
  b = "";
  sign = "";
  finish = false;
  out.textContent = 0;
}

function handleDigitClick(key) {
  if (b === "" && sign === "") {
    a += key;
    out.textContent = a;
  } else if (a !== "" && b !== "" && finish) {
    b = key;
    finish = false;
    out.textContent = b;
  } else {
    b += key;
    out.textContent = b;
  }
  console.table(a, b, sign);
}

function handleActionClick(key) {
  sign = key;
  out.textContent = sign;
  console.table(a, b, sign);
}

function handleEqualsClick() {
  if (b === "") b = a;
  switch (sign) {
    case "+":
      a = +a + +b;
      break;
    case "-":
      a = a - b;
      break;
    case "X":
      a = a * b;
      break;
    case "÷":
      if (b === "0") {
        out.textContent = "Ошибка";
        a = "";
        b = "";
        sign = "";
        return;
      }
      a = a / b;
      break;
  }
  finish = true;
  out.textContent = a;
  console.table(a, b, sign);
}

document.querySelector(".ac").onclick = clearAll;

document.querySelector(".buttons").onclick = (event) => {
  if (!event.target.classList.contains("knop")) return;
  if (event.target.classList.contains("ac")) return;

  out.textContent = "";
  const key = event.target.textContent;

  if (digit.includes(key)) {
    handleDigitClick(key);
    return;
  }

  if (action.includes(key)) {
    handleActionClick(key);
    return;
  }

  if (key === "=") {
    handleEqualsClick();
  }
};
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Калькулятор</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <script src="index.js" defer></script>
    <div class="cal">
      <div class="screen" maxlength="10">
        <p data-output>0</p>
      </div>
      <div class="buttons">
        <button class="knop ac pink">AC</button>
        <button class="knop delete pink"><</button>
        <button class="knop percent pink">%</button>
        <button class="knop division pink">÷</button>

        <button class="knop one">1</button>
        <button class="knop two">2</button>
        <button class="knop three">3</button>
        <button class="knop X pink">X</button>

        <button class="knop four">4</button>
        <button class="knop five">5</button>
        <button class="knop six">6</button>
        <button class="knop minus pink">-</button>

        <button class="knop seven">7</button>
        <button class="knop eight">8</button>
        <button class="knop nine">9</button>
        <button class="knop plus pink">+</button>

        <button class="knop zero">0</button>
        <button class="knop dot">.</button>
        <button class="knop equals pink">=</button>
      </div>
    </div>
  </body>
</html>

querySelector('.screen p') лучше заменить на что-то более осознанное, в один селектор.Лучше ввести data-атрибуты, типа data-action

Прошу помочь

Вот такая разметка

<div class="screen" maxlength="10">
	<p class="out">0</p>
</div>

и такой js код сработают

const out = document.querySelector(".out");

Мне не понятна причина сомнений и вопроса. В целом почти всегда (99% случаев) достаточно ввести уникальный css класс и использовать его для получения ссылки на DOM элемент.

Это хорошая и правильная идея. Она “разводит” в стороны несвязанные концепции: то что отображается пользователю не влияет на операцию. Хоть вставляй картинки или текст в кнопки, с моделированием операции через data- все продолжит работать.

Вот как она примерно реализуется: https://jsfiddle.net/xd95uwop/

Разметка

<button data-action="ac" class="knop ac pink">AC</button>
<button data-action="del" class="knop delete pink">&lt;</button>
<button data-action="%" class="knop percent pink">%</button>
<button data-action="/" class="knop division pink">÷</button>
<!-- ... -->
<button data-action="1" class="knop one">1</button>
<button data-action="2" class="knop two">2</button>
<button data-action="3" class="knop three">3</button>

Код:

[...document.querySelectorAll('[data-action]')].forEach((actionNode) => {
	actionNode.addEventListener('click', () => {
  	const operation = actionNode.dataset.action
    console.log('operation', operation)
  })
})

Обрати внимание что тип значения actionNode.dataset.action всегда будет строковым, даже если в датф-аттрибут записана цифра.

Код не обязательно переписывать, можно интегрировать кусок который отвечает за доступ к дата аттрибутам и интегрировать в районе const key = event.target.textContent;. По сути мой пример делает тоже самое, доставая значение для key из дата-аттрибута.

Принял, спасибо вам за помощь!