Div class is causing “Script error”

I’m receiving this error message when I click on the red play image:

Cannot read property ‘classList’ of null"

“Script error.”

How would I be able to keep .containerb in the code without removing it?

How would I fix that issue

Would this be called something else instead?
const thewrap = cover.parentElement.querySelector(".containera");

How it works is, you click on the red play image and the page after is supposed to be visible with a video.

https://jsfiddle.net/4t0qu32k/

When .containerb is removed from the code, the whole code works.

Meaning, when you click on the red play image it opens.
https://jsfiddle.net/j0wu3ghz/

	<div class="containerb">
		<svg class="play" width="100%" height="100%" viewBox="0 0 64 64">
			<path d="M25.6,46.4L44.8,32L25.6,17.6V46.4z M32,0C14.3,0,0,14.3,0,32s14.3,32,32,32s32-14.3,32-32S49.7,0,32,0z
      M32,57.6C17.9,57.6,6.4,46.1,6.4,32S17.9,6.4,32,6.4S57.6,17.9,57.6,32S46.1,57.6,32,57.6z" />
		</svg>

	</div>
<div class="outer">
  <div class="containera hide">
    <div class="curtain">
      <div class="ratio-keeper">
        <div class="wrap">
          <div class="video video-frame"></div>
        </div>
        <div class="panel-left"></div>
        <div class="panel-right"></div>
      </div>
    </div>
  </div>

  <div class="containerb">
    <svg class="play " width="100%" height="100%" viewBox="0 0 64 64">
      <path d="M25.6,46.4L44.8,32L25.6,17.6V46.4z M32,0C14.3,0,0,14.3,0,32s14.3,32,32,32s32-14.3,32-32S49.7,0,32,0z
      M32,57.6C17.9,57.6,6.4,46.1,6.4,32S17.9,6.4,32,6.4S57.6,17.9,57.6,32S46.1,57.6,32,57.6z" />
    </svg>

  </div>

</div>
(function manageCover() {

  function show(el) {
    el.classList.remove("hide");
  }

  function hide(el) {
    el.classList.add("hide");
  }

  function coverClickHandler(evt) {
    const cover = evt.currentTarget;
    hide(cover);
    const thewrap = cover.parentElement.querySelector(".containera");
    show(thewrap);
    const curtain = document.querySelector(".curtain");
    curtain.classList.add("slide");
  }

  const cover = document.querySelector(".play");
  cover.addEventListener("click", coverClickHandler);
}());

When you go with cover.parentElement you are going up one level to the immediate parent. Meaning the wrapping div around your svg with class play and then searching for containera from the point of outer. This works in your second example because your svg’s parent is already outer. In the first example, your parent is containerb and outer is up another level (containera and containerb are children of outer). If you wish to go up another level you can just use parentElement again.

In your first example change it to cover.parentElement.parentElement.querySelector(".containera").

Also another thing I noticed is that your first example you have class="play " notice the space. Make sure you don’t introduce spaces into the class otherwise you can run into issues selecting it and targeting it.

Enjoy! :slight_smile:

2 Likes

That works, but jslint is saying the line is more than 80 characters long.

Is there a way I can split it in half so the line is shorter?

const thewrap = cover.parentElement.parentElement.querySelector(".containera");

Well of course, put it in a variable and call parentElement on the variable.

let thewrap = cover.parentElement.parentElement;
thewrap = thewrap.querySelector(".containera");
1 Like

What about doing it this way?
This would make it shorter.

Is that good or not?

const thewrap = cover.parentElement
.parentElement.querySelector(".containera");

Unless I’m missing something there is only one .containera in this page. Why do you need to find it that way. It makes no sense in this case.

You can simply find it with

const thewrap = document.querySelector(".containera");

Even if there was more than one containera in the page then you would not find a single instance with your method because the parent element would be .outer which contains the whole page anyway and not just a single containera.

Yes, there is in that page. There are going to be other ones though called containerb and containerc and containerd. Yes, that isn’t a good naming scheme.

if you have a local or a relative reference, then it’s usually best to work in relation to that so that things keep on working when there are multiple ones around.

2 Likes

I asked that question here:

Asking the difference between:

.docunment

vs.

name.parentElement.querySelector

And was told this:

What happens if I add more then 1, what if I want to add more videos?

Then I would need to add another and another.

Staying within the current framework would mean using:

const thewrap = cover.parentElement

My comment about work being kept to a minimum was more in regard to code inside of handler functions.

   function coverClickHandler(evt) {
     ...
     wrap.classList.remove("hide");
   }

versus this:

   function coverClickHandler(evt) {
     ...
     show(thewrap);
   }

The second one being preferred.

Separately in regard to using document, the less you use it, and prefer to use instead other things that you already have to access things, the better.

1 Like

When it’s likely that multiple of something may exist on the page, it’s usually best to for each thing to have one container that things are accessed from. That way you can have multiple containers on the page and everything still works.

2 Likes

Let me see if I can get this CSS stuff right.

ID attributes such as #header are okay

<!-- A unique id here is okay, because there's only ever going to be one header -->
<div id="header">

Even though a id for a single thing is okay, it’s better to use a class name as that plays nicer when it comes to things like CSS specificity.

<!-- A class tends to be better than an id, even for a single item -->
<div class="header">

but when there’s likely to be more than one of something such as #container then ID attributes are bad for them. Expanding out to #containera and #containerb are especially bad because then you are forced to copy the styles too.

<!-- Multiple identifier for similar things is bad -->
<div id="containera">...</div>
<div id="containerb">...</div>
<div id="containerc">...</div>

When there is likely to be more than one of something, that’s where classnames become very useful, giving all containers a class of .container.

<div class="container">...</div>
<div class="container">...</div>
<div class="container">...</div>

CSS folks, chip in if there’s more to add.

1 Like

I think you covered it well. :slight_smile:

The only thing I would add is that you can use the same class where property values are shared and then use another class for the differences.

e.g.

<div class="container curtains">...</div>
<div class="container buttons">...</div>
<div class="container initial">...</div>

This is what I keep saying to you and you have to build the page with the end goal in mind. You can’t say what if I want more videos and then change the format of how each video is presented. Or suddenly have one with a load of buttons and one with curtains and one with flying elephants or whatever takes your fancy at the time. :slight_smile:

You have to build with this goal in mind from the start.

The current demo has 3 different sections. One has a curtain effect, one has multiple buttons and the other section holds 2 buttons to trigger either of the two above.

As @Paul_Wilkins said the ideal would be to have the button that triggers the player to be in the same container so you can easily find it without explicit references but that would need a new approach with the html.

If that’s your goal then I would change the html to this structure.

<audio></audio>
<div class="outer">
  <div class="container with-curtain">
    <svg class="playa thePlay" width="100%" height="100%" viewBox="0 0 64 64">
      <path d="M25.6,46.4L44.8,32L25.6,17.6V46.4z M32,0C14.3,0,0,14.3,0,32s14.3,32,32,32s32-14.3,32-32S49.7,0,32,0z
      M32,57.6C17.9,57.6,6.4,46.1,6.4,32S17.9,6.4,32,6.4S57.6,17.9,57.6,32S46.1,57.6,32,57.6z">
    </svg>
    <div class="inner-container curtain">
      <div class="ratio-keeper">
        <div class="wrapa">
          <div class="video video-frame"></div>
        </div>
        <div class="panel-left"></div>
        <div class="panel-right"></div>
      </div>
    </div>
  </div>
  <!-- end container -->
  <div class="container with-buttons">
    <svg class="playb thePlay" width="100%" height="100%" viewBox="0 0 64 64">
      <path d="M25.6,46.4L44.8,32L25.6,17.6V46.4z M32,0C14.3,0,0,14.3,0,32s14.3,32,32,32s32-14.3,32-32S49.7,0,32,0z
      M32,57.6C17.9,57.6,6.4,46.1,6.4,32S17.9,6.4,32,6.4S57.6,17.9,57.6,32S46.1,57.6,32,57.6z" />
    </svg>
    <div class="inner-container button-container">
      <div class="playButton b1" data-audio="http://getradio.me/svoefm">
        <div class="button">
          <div class="light"></div>
          <div class="dots"></div>
          <div class="characters"></div>
          <div class="shine"></div>
          <div class="shadow"></div>
        </div>
      </div>
      <div class="playButton b2" data-audio="ttp:/fm1.hostingradio.ru:14536/rock90.mp3">
        <div class="button">
          <div class="light"></div>
          <div class="dots"></div>
          <div class="characters"></div>
          <div class="shine"></div>
          <div class="shadow"></div>
        </div>
      </div>
      <div class="playButton b3" data-audio="http:/fm1.hostingradio.ru:14536/rock90.mp3">
        <div class="button">
          <div class="light"></div>
          <div class="dots"></div>
          <div class="characters"></div>
          <div class="shine"></div>
          <div class="shadow"></div>
        </div>
      </div>
      <div class="playButton b4" data-audio="http:/fm1.hostingradio.ru:14536/rock90.mp3">
        <div class="button">
          <div class="light"></div>
          <div class="dots"></div>
          <div class="characters"></div>
          <div class="shine"></div>
          <div class="shadow"></div>
        </div>
      </div>
      <div class="playButton b5" data-audio="http:/fm1.hostingradio.ru:14536/rock90.mp3">
        <div class="button">
          <div class="light"></div>
          <div class="dots"></div>
          <div class="characters"></div>
          <div class="shine"></div>
          <div class="shadow"></div>
        </div>
      </div>
      <div class="playButton b6" data-audio="http:/fm1.hostingradio.ru:14536/rock90.mp3">
        <div class="button">
          <div class="light"></div>
          <div class="dots"></div>
          <div class="characters"></div>
          <div class="shine"></div>
          <div class="shadow"></div>
        </div>
      </div>
      <div class="playButton b7" data-audio="http:/fm1.hostingradio.ru:14536/rock90.mp3">
        <div class="button">
          <div class="light"></div>
          <div class="dots"></div>
          <div class="characters"></div>
          <div class="shine"></div>
          <div class="shadow"></div>
        </div>
      </div>
      <div class="playButton b8" data-audio="http:/fm1.hostingradio.ru:14536/rock90.mp3">
        <div class="button">
          <div class="light"></div>
          <div class="dots"></div>
          <div class="characters"></div>
          <div class="shine"></div>
          <div class="shadow"></div>
        </div>
      </div>
      <div class="playButton b9" data-audio="http:/fm1.hostingradio.ru:14536/rock90.mp3">
        <div class="button">
          <div class="light"></div>
          <div class="dots"></div>
          <div class="characters"></div>
          <div class="shine"></div>
          <div class="shadow"></div>
        </div>
      </div>
    </div>
  </div>
  <!-- end container -->
</div>

I have hard coded just the blue button for example

The js I changed is this:

  function coverClickHandler(evt) {
    const cover = evt.currentTarget.parentElement;
    cover.classList.add("active");
    //hide all containers except this one
    // should really loop through all containers except the active one
    // I just hard coded it for example
    document.querySelector(".container.with-buttons").classList.add("hide");
  

I only did that for example and the last part would need to be done properly :slight_smile: Note that we don’t need to add the slide class because the active class on the container can be used instead (as I mentioned about a 1000 times). The above routine could then be used for any container. You wouldn’t need to duplicate it if done properly.

Each button is inside the container that it refers to. All you have to do on click the button and it adds an active class to its own container and then hide the other containers. This could scale up and down with as many buttons as required without explicit relationships.

1 Like

Both panels will open now but I have no idea about the player code as only the first red button plays anything. I haven’t touched that section.

Hopefully that will give you and Paul something to work on while I’m away next week :slight_smile:

I’m only doing videos, no more of the other stuff.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.