-1

I am coding a blog with Nuxt.js, and I am connected with the API of ButterCMS.
I want to get the date (text) of the post, and slice it. My problem is that it return an error : TypeError: null is not an object (evaluating 'document.querySelector(".date").textContent'). When I execute the same code in the JS Console, it is working. I already tried to add a event listener to the load of the page, but it didn't change anything. Here is my code :

document.querySelector(".date").textContent.slice(0, 29);
<template>
  <div id="blog-home">
    <div class="recent-article-feed">
      <div v-for="(post, index) in posts" :key="post.slug + '_' + index">
        <router-link :to="'/blog/' + post.slug">
          <div class="article">
            <div class="dark-window">
              <div class="text-box">
                <h2 class="title">{{ post.title }}</h2>
                <div>
                  <span class="author">
                    <i class="fa-solid fa-user"></i> Par Maxime Hamou
                  </span>
                  &#8729;
                  <span class="date">
                    <i class="fa-solid fa-calendar-days"></i>
                    {{ post.published }}
                  </span>
                </div>
                <p class="description">
                  {{ post.summary }}
                </p>
                <p class="read">Lire l'article &#x2192;</p>
              </div>
            </div>
          </div>
        </router-link>
      </div>
    </div>
  </div>
</template>

<style>
@import url("../css/index.css");
@import url("../css/components/recent-article-feed.css");
</style>

<script>
import { butter } from "~/plugins/buttercms";
export default {
  data() {
    return {
      posts: [],
    };
  },
  methods: {
    getPosts() {
      butter.post
        .list({
          page: 1,
          page_size: 10,
        })
        .then((res) => {
          // console.log(res.data)
          this.posts = res.data.data;
        });
    },
  },
  created() {
    this.getPosts();
  },
};
</script>
MH info
  • 52
  • 8
  • 3
    The date may be loaded from an API call or js of your dependency, and your code might be executed before the date is actually loaded. In the network tab of Chrome dev tools, you can see if the date is loaded from an API call. – Marco Sep 26 '22 at 13:08
  • Where in that code did you place the `document.querySelector(".date").textContent.slice(0, 29);`? This very much sounds like a duplicate of [Why does jQuery or a DOM method such as getElementById not find the element?](https://stackoverflow.com/q/14028959/1048572) – Bergi Sep 26 '22 at 13:25
  • This code looks like you're using Vue.js. Can you confirm? – Bergi Sep 26 '22 at 13:25
  • You should not be using `document.querySelector` at all to access the date! Just use `this.posts[0].published`! – Bergi Sep 26 '22 at 13:25

2 Answers2

0

The first thing is to make sure we understand what the error is trying to tell us.

TypeError: null is not an object (evaluating 'document.querySelector(".date").textContent')

Generally when you see something (like a variable or element) being labeled as null or undefined that means it does not exist. This could be due to a timing issue (being called before it is created/ready) or due to a scope issue (like a variable being called outside of its declared scope/function).

Because you are using an API and template-based code, it is almost certainly a timing issue. You did mention you tried adding an event listener to the page load, however this only works if your element/data is set before the page load, which almost certainly does not happen.

Realistically, the page will always complete its load event first, and then data from the API will be returned. And after all of this, then your template-based code will plug in the data to your page. This can be assumed because your template-based code is using promises (indicated by the .then() method).

So how do you fix this? You have to wait for the element you need to actually exists. The best way I found to do this is with something like a MutationObserver. You can create a function that uses a Mutation Observer on the page and whenever the content of the page changes, it fires some code. Here you can check to see if your date element exists or not. When it does finally exists, this function can return a promise, which allows you to now finally execute some code (like getting the textContent of said element).

So try adding something like this:

// This declares the function to wait for an element to exist
const _ElementAwait = el => {
  return new Promise(resolve => {
    if(document.querySelector(el)) return resolve(document.querySelector(el));

    const observer = new MutationObserver(ml => {
      if(document.querySelector(el)) {
        resolve(document.querySelector(el));
        observer.disconnect();
      }
    });

    observer.observe(document.body, { childList: true, subtree: true });
  });
}

// Here we call the function to wait for an element with the class 'date'
_ElementAwait(".date").then(el => {
  // Once it does exist, the element is returned and we can get the textContent
  el.textContent.slice(0, 29);
});
EssXTee
  • 1,783
  • 1
  • 13
  • 18
  • "*You have to wait for the element you need to actually exists.*" - yes. "*The best way I found to do this is with something like a MutationObserver.*" - no. You use a mutation observer is some code that you cannot control does change the dom. But the OP is coding the website themselves. They should just put their code in the place that does create the element – Bergi Sep 26 '22 at 13:28
0

I just added a setTimeout and it work. Thank you for your help. Here is my new code :

setTimeout(function(){
  const date = document.querySelector(".date").textContent.slice(0, 29);
  document.querySelector(".date").textContent = "";
  document.querySelector(".date").textContent = date;
}, 1000);

EDIT :
I remplaced the setTimeout by butter.page.retrieve directly in the page where I want to execute the script (found in ButterCMS docs). When the page "simple-page" is retrieved, the code that add my script is executed. Here the new code :

butter.page.retrieve("*", "simple-page").then(() => {
  const createScript = document.createElement("script");
  createScript.src = "../script/post-informations.js";
  document.head.appendChild(createScript);
});
MH info
  • 52
  • 8