Several Subtle Differences Between Common DOM APIs

Each section shows a small code sample followed by a live result that runs the same idea directly in the page.

1. innerText vs. textContent

Code:
const box = document.getElementById('text-sample');

document.getElementById('innerText-output').textContent = box.innerText;
document.getElementById('textContent-output').textContent = box.textContent;
Result:
First visible line Hidden words

Second visible line

innerText
textContent

The hidden span uses display: none. innerText skips it; textContent still reads it.

2. getAttribute vs. dataset

Code:
const button = document.getElementById('dataset-button');

const result = [
  `button.getAttribute('DATA-author'): ${button.getAttribute('DATA-author')}`,
  `button.dataset.AUTHOR: ${button.dataset.AUTHOR}`,
  `button.dataset.author: ${button.dataset.author}`,
  `button.getAttribute('data-article-author'): ${button.getAttribute('data-article-author')}`,
  `button.dataset.articleAuthor: ${button.dataset.articleAuthor}`,
  `button.dataset['article-author']: ${button.dataset['article-author']}`
].join('\n');

document.getElementById('dataset-output').textContent = result;
Result:

3. getElementById vs. querySelector

Code:
const id = document.getElementById('id-input').value;
const lines = [];

lines.push(`getElementById('${id}')`);
lines.push(String(document.getElementById(id)));

try {
  lines.push(`querySelector('#${id}')`);
  lines.push(String(document.querySelector(`#${id}`)));
} catch (error) {
  lines.push(error.name + ': ' + error.message);
}

document.getElementById('selector-output').textContent = lines.join('\n');
Result:
Target element: id="thanksForShare"

4. append vs. appendChild

Code:
const stage = document.getElementById('append-stage');
stage.textContent = '';

const node1 = document.createElement('span');
node1.className = 'chip';
node1.textContent = 'node 1';

const node2 = document.createElement('span');
node2.className = 'chip';
node2.textContent = 'node 2';

stage.append(node1, ' plain string ', node2);

try {
  stage.appendChild(' plain string ');
} catch (error) {
  document.getElementById('append-output').textContent =
    `appendChild(' plain string ') -> ${error.name}`;
}
Result:

append() accepts multiple nodes and strings. appendChild() accepts one Node, so passing a string throws.

5. scrollIntoView vs. scrollIntoViewIfNeeded

Code:
const frame = document.getElementById('scroll-frame');
const target = document.getElementById('scroll-target');

document.getElementById('scrollIntoView-button').onclick = () => {
  target.scrollIntoView({
    block: 'center',
    behavior: 'smooth'
  });
};

document.getElementById('scrollIfNeeded-button').onclick = () => {
  if (target.scrollIntoViewIfNeeded) {
    target.scrollIntoViewIfNeeded(true);
  } else {
    const frameRect = frame.getBoundingClientRect();
    const targetRect = target.getBoundingClientRect();
    const visible = targetRect.top >= frameRect.top && targetRect.bottom <= frameRect.bottom;

    if (!visible) {
      target.scrollIntoView({
        block: 'center',
        behavior: 'auto'
      });
    }
  }
};
Result:
Scroll area starts here
Target Element
More content below

scrollIntoView() can use smooth scrolling and precise alignment. The fallback shown here mimics scrollIntoViewIfNeeded(true) when the browser does not support it.

6. Quick Rule of Thumb

Code:
const summary = [
  'Use textContent when you need raw text, including hidden text.',
  'Use dataset with camelCase for data-* properties.',
  'Use getElementById when the value is an ID, especially if it may contain selector syntax.',
  'Use append when adding multiple nodes or strings.',
  'Use scrollIntoView when you need configurable scrolling.'
];

document.getElementById('summary-output').innerHTML =
  summary.map(item => `<li>${item}</li>`).join('');
Result: