0

I was just trying to use document methods like getElementsByClassName, getElementsByTagName, getElementById, and consoling the output. However strange thing is that only getElementById is not working, it logs null. I have tried className with App and TagName h1, works perfectly. Can anyone shed light on this? This is code sandbox [ https://codesandbox.io/s/813mnx1vq2 ].

Below is my code of App which I am rendering,

function App() {

  { console.log(document.getElementById('heading1')) }

  /*
  { console.log(document.getElementsByTagName("h1")) }
  // Output: HtmlCollection array which contains element with id=heading1
  */

  /*
  { console.log(document.getElementsByClassName("App")) }
  // Output: HtmlCollection array which contains div.App element
  */

  return (
    <div  className="App">
      <h1 id="heading1">Hello CodeSandbox</h1>

      <h2>Start editing to see some magic happen!</h2>

    </div>
  );

}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

PS Edit: When is NodeList live and when is it static? . Thanks to Jonas.

Vivek
  • 1,375
  • 2
  • 15
  • 24
  • 1
    At the time that you run the selection, React has not yet rendered the component, and so there is nothing in the DOM matching `heading1` – Matthew Herbst Dec 10 '18 at 06:36
  • then how it works for TagName and ClassName? – Vivek Dec 10 '18 at 06:38
  • I honestly have no idea - AFAIK, it shouldn't – Matthew Herbst Dec 10 '18 at 06:39
  • Is it because of asynchronous behavior of render function? – Vivek Dec 10 '18 at 06:41
  • I think you're missing some concepts here. If I understand correctly what you're trying or aiming to do. Then you should add a property to the component and update/access the specific value via the property, vs trying to select an element inside it and then updating it via traversing the DOM & then attempting to update the specific node element. – Rohan Büchner Dec 10 '18 at 06:42
  • Both `getElementsByTagName` and `getElementsByClassName` are synchronous, so regardless of if `render` is async or not, both should not give anything back because they run _and finish_ before the `return` from the `render` ever happens. – Matthew Herbst Dec 10 '18 at 06:43
  • 1
    @RohanBüchner I just wanted to know why it's behaving like this. – Vivek Dec 10 '18 at 06:43

2 Answers2

5

There are two types of different data structures returned from these methods, one is a NodeList, the other one is one single node. The special thing about NodeLists is that they are live, which means that if a node gets added to the DOM it also gets added to the NodeList. In the console you see a live version of the things logged, therefore you also see elements that are not yet in the DOM with getElementsByTagName and you get no result with getElementByID.

Nevertheless you shouldn't use them at all when using React.

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • Yeah, I know I shouldn't be using. I was just experimenting with different things. Can you provide some reference for it to know more about? – Vivek Dec 10 '18 at 06:54
  • Amazing didn't know that thing. Thanks again – Vivek Dec 10 '18 at 07:01
  • 2
    NodeLists aren't necessarily live. Typically, older NodeLists like those from *getElementsByTagName* and *childNodes* are live, but newer ones from *document.querySelectorAll* are static. – RobG Dec 10 '18 at 07:03
  • 1
    I also didn't actively realize what was happening (one of those things I just took for granted/never looked into). But here is an interesting visualisation. If you look at the 2 sets of logs (in this fork of the sample). https://codesandbox.io/s/6vkjw1rl3z You'll see the 1st set, as @Vivek had them, the array *is* empty until you expand it in the console (upon expansion the console *does* show the element which speaks to this NodeList behaviour), and the second log set prints the output as you expected, as by then, React has updated the DOM. – Rohan Büchner Dec 10 '18 at 07:09
  • @RobG true that. Just for ref, https://stackoverflow.com/questions/28163033/when-is-nodelist-live-and-when-is-it-static – Vivek Dec 10 '18 at 07:13
2

The DOM node is not available by the time getElementById is called.

A simple solution is to convert the component to a class component and use the componentDidMount lifecycle method.

class App extends React.Component {
   componentDidMount() {
       console.log(document.getElementById('heading1'))
   }
   render() {
      return (
        <div  className="App">
          <h1 id="heading1">Hello CodeSandbox</h1>

          <h2>Start editing to see some magic happen!</h2>

        </div>
      );
   }
}

In case you strictly want functional component, there is a library which allows access to lifecycle methods inside functional component called react-pure-lifecycle