Error adding and removing Uncaught Typeerror class: Cannot read Property 'classList' of null

Asked

Viewed 967 times

1

I created a script to add and remove a class, it works, but sometimes when there is this change of classif this error happens: Uncaught TypeError: Cannot read property 'classList' of null.

Can anyone say what can be and if there is a solution? For example, when the error happens show the last data clicked, do not know.

  const selectMapID = document.querySelectorAll('.map a');
  const selectArticles = document.querySelectorAll('.map article');
  selectArticles[1].classList.add('ativo');

  selectMapID.forEach(item => {
    item.addEventListener('click', showState);
  })

  function showState(event) {
    event.preventDefault();
    const element = event.target;
    const id = element.getAttribute('id');
    const article = document.getElementById(id);

    selectArticles.forEach( (section) => {
      section.classList.remove('ativo');
    } );
    article.classList.add('ativo');
  }
  • Which line of error?

  • Looking at the code, it seems to me that you are trying to grab the id of a clicked <a> and add the . class active in a <article> with the same id. This is not going to work anyway. Put the HTML code tb so we can see.

  • That’s the idea, the problem is I need to do it!

2 answers

1

About the error

The error shown says that the classList property of "null could not be found".

Why the variable is set to "null"?

on the line:

const id = element.getAttribute('href').replace('#', '');

you try to redeem the value of the attribute "href" from the anchor that was clicked, and on the next line:

 const article = document.getElementById(id);

you pass the received value as parameter of the getElementById médoto which for some reason returns null, indicating that there is no element in the DOM with that id. The possible causes for this may be:

  1. typo error - you may have typed the same words with high/low box letters on your link, or some typo and on your element that should be rescued by getElementById the value differs;
  2. missing value in any of the references - you may have forgotten to put the id on the anchor or element to be rescued by getElementById;

Solution

Just check if the values are correct in both parts.

Improvements

I would like to suggest some improvements to your code that can help in performance and avoid some possible bugs.

Avoid performing the same tasks in vain

In the function performed at each click data is stored and rescued from the gift, but this should only happen at each click of a "non-active" reference, for example:

  1. Imagine the anchors "A", "B" and "C" and the anchor "A" is active;
  2. The function triggered by the click should only perform the proper tasks if the click comes from the anchors "B" or "C" at this time, because "A" is already active and consequently its reference also.

Example:

const activeState = {
  previous: 0,
  current: 0,
};

function updateActiveState(current) {
  activeState.previous = activeState.current;
  activeState.current = current;
}

function onLinkClick(e) {
  e.preventDefault();
  const target = event.target;
  const eIndex = parseInt(target.getAttribute('data-index'));

  if(activeState.current !== eIndex) {
    // do something
  }
}

In this example I created an object to serve as "state" to know which element is currently active and which element was previously clicked.

Benefit: centralization of data decrease logic to add and/or remove classes.

Cache the elements to be manipulated

In its function executed every click on the line:

const article = document.getElementById(id);

DOM is required to rescue the element by its id which requires a certain use of browser memory. To improve this, I suggest creating a cache in the previous scope of the function and using it to rescue these elements whenever you need it instead of going to the DOM again. For example:

// Resgata do DOM apenas uma vez o elemento que contém os artigos a serem manipulados
const articlesSection = document.querySelector('.articles');

// Resgata do Objeto em cache todos os elementos com a classe "article"
const articles = articlesSection.querySelectorAll('.article');

// Resgata do Objeto em cache o elemento com o id especificado
const article1 = articlesSection.querySelector('#article1');
const article2 = articlesSection.getElementById('#article2');

Benefits: performance

Dry the example with the improvements applied

const links = document.querySelectorAll('nav .link');
const articlesSection = document.querySelector('.articles');
const articles = articlesSection.querySelectorAll('.article');

const activeState = {
  previous: 0,
  current: 0,
};

function updateActiveState(current) {
  activeState.previous = activeState.current;
  activeState.current = current;
}

function onLinkClick(e) {
  e.preventDefault();
  const target = event.target;
  const eIndex = parseInt(target.getAttribute('data-index'));

  if(activeState.current !== eIndex) {
    updateActiveState(eIndex);

    const currentActive = activeState.current;
    const previousActive = activeState.previous;
    const currentRef = target.getAttribute('data-ref');
    const previousRef = links[previousActive].getAttribute('data-ref');
    const currentArticle = articlesSection.querySelector("#" + currentRef);
    const previousArticle = articlesSection.querySelector("#" + previousRef);
    const activeClass = 'active';

    target.classList.add(activeClass);
    links[previousActive].classList.remove(activeClass);
    
    currentArticle.classList.add(activeClass);
    previousArticle.classList.remove(activeClass); 
    
  }
}

links.forEach((link, idx) => {
  link.classList.add(idx === 0 ? 'active' : null);
  link.setAttribute('data-index', idx);
  link.addEventListener('click', onLinkClick);
});

articles.forEach((article, idx) => {
  article.classList.add(idx === 0 ? 'active' : null);
  article.setAttribute('data-index', idx);
});
html, body {
  margin: 0;
  padding: 0;
  height: 100%;
}

.container {
  display: flex;
  flex-direction: row;
  align-items: start;
  height: 100%;
}

nav, .articles {
  box-sizing: border-box;
  height: 100%;
}

nav {
  display: flex;
  flex-direction: column;
  background: rgba(0, 0, 0, 0.7);
  min-width: 150px;
}

nav .link, nav .link:visited {
  color: #cccccc;
  padding: 0.5em 1em;
  text-decoration: none;
}

nav .link:not(:nth-child(1)), nav .link:visited {
  border-top: 1px dotted rgba(255, 255, 255, 0.2)
}

nav .link:hover, nav .link:visited:hover, nav .link.active {
  color: #ffffff;
}

.articles {
  padding: 1em;
  background: rgba(0, 0, 0, 0.03);
}

.articles .article:not(:nth-child(1)) {
  padding-top: 10px;
}

.articles .article:not(.active) {
  opacity: 0.3;
}


.article p {
  margin: 0;
}
<div class="container">
  <nav>
    <a href="#" class="link" data-ref="one">Lorem.</a>
    <a href="#" class="link" data-ref="two">Ullam?</a>
    <a href="#" class="link" data-ref="three">Amet.</a>
  </nav>
  
  <section class="articles">
    <div class="article" id="one">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Vero, laboriosam.</p>
    </div>
    <div class="article" id="two">
      <p>Ab explicabo voluptatibus corporis quas aliquid officiis ratione accusamus unde.</p>
    </div>
    <div class="article" id="three">
      <p>Exercitationem assumenda quis quidem, est sapiente nihil provident at sunt!</p>
    </div>
  </section>
</div>  

  • Face very fuck your answer, I will analyze every step and start using.

  • Success, boy!

1


I made small modifications to your code so that the error is no longer seen. Take a look:

const selectMapID = document.querySelectorAll('.map a');
const selectArticles = document.querySelectorAll('.map article');

selectArticles[1].classList.add('ativo');

selectMapID.forEach(item => {
  item.addEventListener('click', showState);
});

function showState(event) {
  event.preventDefault();

  const element = event.target;
  const id = element.getAttribute('href').replace('#', '');
  const article = document.getElementById(id);

  selectArticles.forEach(section => {
    section.classList.remove('ativo');
  });
  article.classList.add('ativo');
}
.ativo {
  color: green
}
<div class="map">
  <article id="article1">Article I</article>
  <article id="article2">Article II</article>
  <article id="article3">Article III</article>
  <a href="#article1">I</a>
  <a href="#article2">II</a>
  <a href="#article3">III</a>
</div>

  • This code helped a lot, because before the information disappeared from the screen when the error occurred, but still happens a different error Uncaught Typeerror: Cannot read Property 'replace' of null, but at least the information does not disappear

  • just for the record I am selecting a map that is in SVG.

  • 1

    @Randysmachado, you need to add the attribute href on the tag <a> pointing to the id of article according to the HTML example I did.

  • yes, I did it!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.