Getting initial to work at the top of the code?

Firstly, as I’ve said before, PaulOB is not only more of an expert on CSS than I am, but he’s more of an expert on CSS than most folk

In my initial question, I said:

I have 2 questions for you

I was waiting to hear from him because he made the code, so he would have a better understanding.

And you did explain for me the usage of these very well,
Now I have a better understanding of how those work.
And I thank you for that.

.title{display:none}
.inactive .title{display:block;}
1 Like

@PaulOB

Javascript is disabled here:

Since the cover is not showing, does that mean I need to fix it?

Is this better, is this what you mean?

I changed this:

<h1 class="title">Links</h1>

.title {
  height: 168px;
  margin: 0;
  background: url("https://i.imgur.com/BBYxKcf.jpg");
  border: 3px solid #0059dd;
  font-family: Tahoma;
  font-weight: bold;
  font-size: 30px;
  color: #0059dd;
  line-height: 100px;
  text-align: center;
  cursor: pointer;
}

To This:

<div class="title" title="OPEN">
    <p>Links</p>
  </div>

.title {
  width: 266px;
  height: 174px;
  background: url("https://i.imgur.com/BBYxKcf.jpg")no-repeat;
  cursor: pointer;
  border: 3px solid #0059dd;
  box-sizing: border-box;
}

.title p {
  font-family: Tahoma;
  font-weight: 900;
  font-size: 30px;
  color: #0059dd;
  line-height: 40px;
  text-align: center;
}

When I enable javascript:

Both these codes are no longer needed, if that’s correct?

.title{display:none;}
.inactive .title{display:block;}

No that means the opposite and the page is usable and defaults to a working page when Js is disabled.

If you removed those css rules then the cover would show and be of no use to any one

This was all explained at the start and you seem to be circling back and fiddling with things that were already tried and tested!

2 Likes

How can it be better? You changed something that worked into something that didn’t work just to save adding a couple of properties.

2 Likes

Oh, so with the javascript disabled, what I want to see are the links, not the cover image?

I think I get it now.


So, this would work too, then.
Just a different way of writing the code.

 <div class="title" title="OPEN">
    <p>Links</p>
  </div>

.title {
  width: 266px;
  height: 174px;
  background: url("https://i.imgur.com/BBYxKcf.jpg")no-repeat;
  cursor: pointer;
  border: 3px solid #0059dd;
  box-sizing: border-box;
}

.title p {
  font-family: Tahoma;
  font-weight: 900;
  font-size: 30px;
  color: #0059dd;
  line-height: 40px;
  text-align: center;
}

.title {
  display: none;
}
.inactive .title {
  display: block;
}

With this code, how come I’m not seeing the links with javascript disabled, and it’s just blank white space? @PaulOB

I’m perplexed by this.

This code has no typed text, it’s only an image.
Shouldn’t the links still be showing with the javascript disabled?
Like how the other code does it?

<div class="cover"></div>

.cover {
  width: 266px;
  height: 174px;
  background: url("https://i.imgur.com/MbJ5O2d.png") no-repeat;
  cursor: pointer;
  border: 3px solid #0059dd;
  box-sizing: border-box;
}

.cover {
  display: none;
}

.inactive .cover {
  display: block;
}

Let’s start indeed. After adding external resources and the test runner, the basic test shows that there’s a conflict between your CSS styles and the reporter. https://jsfiddle.net/pmw57/ffvkbLjw/304/

The title class is used by both of them, so updating your title class so that it is only applied inside of the .wrap class, and the testing is ready to begin. https://jsfiddle.net/pmw57/ffvkbLjw/305/

What test can we use that will accurately tell us that the problem with the links image is fixed? Looking at the console view of the code, I see that class names seem to be correctly added to it, so checking class names will not in this case be of an use.

The problem is that the links image is being displayed when it shouldn’t be displayed, so we can check its computed style, to figure out if it is still showing or not.

  it("hides the title image when it's clicked", function () {
    var title = document.querySelector(".title");
    title.click();
    expect(window.getComputedStyle(title).display).toBe("none");
  });

That gives us a test that accurately shows us what’s failing. https://jsfiddle.net/pmw57/ffvkbLjw/326/
But, when the test is run it changes how the button looks. That’s not good, so we can use an afterEach() function to return things back to how they should be, after each test.

  afterEach(function () {
    var title = document.querySelector(".title");
    title.setAttribute("class", "title");
  });

That’s partly effective, but we really need to return the wrap element back to how it started too.

  afterEach(function () {
    var wrap = document.querySelector(".wrap");
    var title = document.querySelector(".title");
    wrap.setAttribute("class", "wrap inactive");
    title.setAttribute("class", "title");
  });

And the button returns back to looking how it should be, after the test.
We do have some duplication now of the title variable, which is going to get worse as testing occurs.

The best-practice technique of dealing with that is to first declare the variable, then inside of a beforeEach() function assign the variable.

  var wrap;
  var title;
  beforeEach(function () {
   wrap = document.querySelector(".wrap");
   title = wrap.querySelector(".title");
  });

We can now remove those var statements from throughout the rest of the code too.

  afterEach(function () {
    // var wrap = document.querySelector(".wrap");
    // var title = document.querySelector(".title");
    ...
  });
  it("hides the title image when it's clicked", function () {
    // var title = document.querySelector(".title");
    ...
  });

There’s one final piece of cleaning up to do. The test triggered the initialOverlay click handler, which then removed itself from the title image. We can deal with that by having a reset() function in the code, and running that after each test. By running that reset() code, we can then move the setAttribute code out of the test, and in to that reset() function instead where it more appropriately belongs.

To make it easier for us to access the button code, I’ve added the button code to a generic app.

var app = {};
app.buttone = (function iife() {

We can then return from the function, anything that we want to make accessible.

  function reset(container) {
    var title = container.querySelector(".title");
    container.setAttribute("class", "wrap inactive");
    title.setAttribute("class", "title");
    container.removeEventListener("click", playButtonClickHandler);
    container.addEventListener("click", initialOverlayClickHandler);
  }
  
  return {
    reset
  };
}());

Normally all of these changes don’t need to be all made at the same time, but because we are dealing with untested code, it hasn’t been written so that it’s easy to test. Starting with tests when you write code results in code that is much easier to test and maintain too.

For now though, the (currently failing) test is in place, and we are in a good situation to work on making the test pass.
https://jsfiddle.net/pmw57/ffvkbLjw/347/

It’s fixed already.

I’m done working on that code.

The title image is expected to turn from a display of block to none when you click on it, but currently that’s not happening. Why is that not happening?

Investigating the element using the browser development tools, I see that the hide class is being ignored, because there’s a style declaration with greater specificity that takes priority over the hide one.

.wrap .title {
    display: block;
}

That style declaration is not needed and can be removed.

/* .wrap .title {
    display: block;
} */

Does removing that style declaration fix the problem? The test passes, so we are all good to move on to the next thing.

Looking at the browser console, I now see:

Uncaught TypeError: Cannot read property 'querySelector' of undefined
    at getPlay ((index):220)
    at isPlaying ((index):242)
    at togglePlayButton ((index):294)
    at playButtonClickHandler ((index):308)
    at HTMLDivElement.initialOverlayClickHandler ((index):316)
    at jasmine.Spec.<anonymous> ((index):347)
    at jasmine.Block.execute (VM2300 jasmine.js:1064)
    at jasmine.Queue.next_ (VM2300 jasmine.js:2096)
    at VM2300 jasmine.js:2086

Clicking on the getPlay() index I see that the undefined button variable is passed in to that function, so I click on the isPlaying() index from the console, to see that the undefined button variable is passed in to that function, and so on until we get to the playButtonClickHandler() function.

  function playButtonClickHandler(evt) {
    var button = upTo(evt.target, ".playButtone");
    togglePlayButton(button);
  }

What is supposed to happen that is currently not happening? The play button is supposed to start playing which shows the pause icon. That pause icon starts off as hidden, so we can check if that’s visible after clicking the initial image too.

Sometimes it can be tempting to place different tests in the same test, but when something goes wrong it’s much more difficult to figure out which of those tests resulted in the failure. So, a new test is added for this next test.

  it("shows the pause icon when the title is first clicked", function () {
    var pauseIcon = wrap.querySelector(".pausee");
    title.click();
    expect(window.getComputedStyle(pauseIcon).display).not.toBe("none");
  });

Here’s the updated code with that failing test: https://jsfiddle.net/pmw57/ffvkbLjw/373/

We can now work on making that failing test pass.

Looking at the playButtonClickHandler() function, the target element is the title.

Your HTML structure is:

  • audio
  • .wrap
    • .title
    • ul.nav
      • li
        • a
      • and more li’s
    • .playButtone
      • svg
      • svg

When starting from the title, it has two siblings, nav and playButtone. The title has no children, and its parent is wrap.

To get from the title to playButtone, the most reliable way is to go up to its parent called wrap, and then from there to playButtone

  function playButtonClickHandler(evt) {
    var wrap = upTo(evt.target, ".wrap");
    var button = wrap.querySelector(".playButtone");
    togglePlayButton(button);
  }

But the test still doesn’t pass. Why is that? Turning off the previous test by setting it to xit shows that the recent test results in the button automatically playing. That’s not good. The reset() function needs to stop the play button from playing, and set the pause icon to be hidden too.

  function reset(container) {
    var title = container.querySelector(".title");
    var button = container.querySelector(".playButtone");
    ...
    showPlayButton(button);
    pauseAudio(getAudio());

The test now works more properly, and the reset returns things back to normal.
Enabling the other test now gives us two passing tests, and the button seems to be fully working now. https://jsfiddle.net/pmw57/ffvkbLjw/400/

1 Like

Do you remember what this rule-set was for? @PaulOB

.wrap.inactive  a {
  display: none;
}

It used to be this:

.hide, .wrap.inactive a {
	display: none;
}

I separated them into 2 rule-sets.

.wrap.inactive  a {
  display: none;
}

.hide {
  display: none;
}

Can you please tell me if this rule-set is still needed in the code?

Can you also tell me what its original purpose was meant for, what it was supposed to do?

.wrap.inactive  a {
  display: none;
}

added: javascript on

removed: javascript on
https://jsfiddle.net/zj9583L6/7/

added: javascript off
https://jsfiddle.net/zj9583L6/18/

removed: javascript off
https://jsfiddle.net/zj9583L6/20/

I believe that JS was originally removing the inactive class and thus making the element display:block. IIRC you changed the js to add a class instead of removing one to achieve the same effect. It’s probably superfluous to requirements now.

This one is the code you did, different from the above code.

Now what do you say?

Do you have any idea what its intended purpose was?

It doesn’t seem to do anything.

Unless you say I’m wrong.

.wrap.inactive  a {
  display: none;
}

code added: javascript on

code removed: javascript on

code added: javascript off
https://jsfiddle.net/ykeLr3jh/17/

code removed: javascript off
https://jsfiddle.net/ykeLr3jh/18/

body {
  background-color: black;
}

.wrap {
  width: 266px;
  height: 174px;
  overflow: hidden;
  position: relative;
}

.wrap a {
  float: left;
  width: 44px;
  height: 44px;
  border: 3px solid #0059dd;
  margin: 0 4px 12px 0;
}

.wrap a:hover {
  border: 3px solid red;
}

.wrap li:nth-of-type(5n) a {
  margin-right: 0;
}

.wrap li:nth-of-type(15) a {
  position: relative;
  width: 44px;
  height: 44px;
  border: 3px solid #0059dd;
  background: #ffffff;
}

.wrap li:nth-of-type(15) a::before,
.wrap li:nth-of-type(15) a::after {
  content: '';
  position: absolute;
  top: 0;
  width: 14px;
  height: 44px;
}

.wrap li:nth-of-type(15) a::before {
  left: 0;
  background-color: #00ffff;
}

.wrap li:nth-of-type(15) a::after {
  right: 0;
  background-color: #ff00ff;
}

.wrap.inactive a {
  display: none;
}

.title {
  height: 168px;
  margin: 0;
  background: url("https://i.imgur.com/BBYxKcf.jpg");
  border: 3px solid #0059dd;
  font-family: Tahoma;
  font-weight: bold;
  font-size: 30px;
  color: #0059dd;
  line-height: 100px;
  text-align: center;
  cursor: pointer;
}

.title {
  display: none
}

.inactive .title {
  display: block;
}

.title::before,
.title::after {
  content: "";
  position: absolute;
  top: 0;
  left: 86px;
  width: 3px;
  height: 100%;
  background: #0059dd;
}

.title::after {
  left: 177px;
}

.nav {
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav li {
  float: left;
}

.activated .playButtone {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  margin: auto;
  width: 44px;
  height: 44px;
  cursor: pointer;
  border: 3px solid #0059dd;
  fill: #aaff00;
}

.playe {
  position: absolute;
  left: 6px;
  top: 0;
  right: 0;
  bottom: 0;
  margin: auto;
  z-index: -1;
  /* move it under the image*/
}

.pausee {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  margin: auto;
}

.hidee {
  display: none;
}

<div class="wrap">
  <h1 class="title">Links</h1>
  <ul class="nav">
    <li>
      <a href="#"></a>
    </li>
    <li>
      <a href="#"></a>
    </li>
    <li>
      <a href="#"></a>
    </li>
    <li>
      <a href="#"></a>
    </li>
    <li>
      <a href="#"></a>
    </li>
    <li>
      <a href="#"></a>
    </li>
    <li>
      <a href="#"></a>
    </li>
    <li>
      <a href="#"></a>
    </li>
    <li>
      <a href="#"></a>
    </li>
    <li>
      <a href="#"></a>
    </li>
    <li>
      <a href="#"></a>
    </li>
    <li>
      <a href="#"></a>
    </li>
    <li>
      <a href="#"></a>
    </li>
    <li>
      <a href="#"></a>
    </li>
    <li>
      <a href="#"></a>
    </li>
  </ul>
  <audio></audio>
  <div class="playButtone" data-audio="http://hi5.1980s.fm/;">
    <svg class="playe" focusable="false" width="38" height="40" viewbox="0 0 85 100">
      <path d="M81 44.6c5 3 5 7.8 0 10.8L9 98.7c-5 3-9 .7-9-5V6.3c0-5.7 4-8 9-5l72 43.3z">
        <title>PLAY</title>
      </path>
    </svg>
    <svg class="pausee hidee" focusable="false" width="36" height="40" viewbox="0 0 60 100">
      <path d="M0 8c0-5 3-8 8-8s9 3 9 8v84c0 5-4 8-9 8s-8-3-8-8V8zm43 0c0-5 3-8 8-8s8 3 8 8v84c0 5-3 8-8 8s-8-3-8-8V8z">
        <title>PAUSE</title>
      </path>
    </svg>
  </div>
</div>
(function iife() {
  "use strict";

  function show(el) {
    el.classList.remove("hidea");
    el.classList.remove("hideb");
    el.classList.remove("hidec");
    el.classList.remove("hided");
    el.classList.remove("hidee");
    el.classList.remove("hidef");
  }

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

  function upTo(el, selector) {
    while (el.matches(selector) === false) {
      el = el.parentNode;
    }
    return el;
  }

  function hideAllButtons(button) {
    button.querySelectorAll(".playa, .pausea, .initialb, .pauseb, .speakerb, .playb, .playc, .pausec, .speakerc, .playd, .paused, .playe, .pausee, .playf, .pausef").forEach(hide);
  }

  function getPlay(button) {
    return button.querySelector(".playa, .playb, .playc, .playd, .playe, .playf");
  }

  function getPause(button) {
    return button.querySelector(".pausea, .pauseb, .pausec, .paused, .pausee, .pausef");
  }

  function hideInitialOverlay(button) {
    var link = upTo(button, ".wrap");
    link.classList.remove("inactive");
    hide(button.querySelector(".title"));
    button.classList.add("activated");
  }

  function showPlayButton(button) {
    var play = getPlay(button);
    hideAllButtons(button);
    show(play);
    button.classList.remove("active");
  }

  function isPlaying(button) {
    var play = getPlay(button);
    return play.classList.contains("hidea") || play.classList.contains("hideb") || play.classList.contains("hidec") || play.classList.contains("hided") || play.classList.contains("hidee") || play.classList.contains("hidef");
  }

  function pauseAllButtons() {
    var buttons = document.querySelectorAll(".playButtona, .playButtonb, .playButtonc, .playButtond, .playButtone, .playButtonf");
    buttons.forEach(function hidePause(button) {
      if (isPlaying(button)) {
        showPlayButton(button);
      }
    });
  }

  function showPauseButton(button) {
    var pause = getPause(button);
    pauseAllButtons();
    hideAllButtons(button);
    show(pause);
  }

  function getAudio() {
    return document.querySelector("audio");
  }

  function playAudio(player, src) {
    player.volume = 1.0;
    player.setAttribute("src", src);
    player.play();
  }

  function showButton(button, opts) {
    if (opts.playing) {
      showPlayButton(button);
    } else {
      showPauseButton(button);
    }
  }

  function pauseAudio(player) {
    player.pause();
  }

  function manageAudio(player, opts) {
    if (opts.playing) {
      pauseAudio(player);
    } else {
      playAudio(player, opts.src);
    }
  }

  function togglePlayButton(button) {
    var player = getAudio();
    var playing = isPlaying(button);

    showButton(button, {
      playing
    });
    manageAudio(player, {
      src: button.getAttribute("data-audio"),
      playing
    });
  }


  function playButtonClickHandler(evt) {

    var button = upTo(evt.target, ".playButtone");
    togglePlayButton(button);
  }

  function initialOverlayClickHandler(evt) {
    var button = upTo(evt.target, ".wrap");
    hideInitialOverlay(button);
    button.removeEventListener("click", initialOverlayClickHandler);
    button.addEventListener("click", playButtonClickHandler);
  }
  document.querySelector(".wrap").classList.add("inactive");
  var playButton = document.querySelector(".wrap");
  playButton.addEventListener("click", initialOverlayClickHandler);
}());

As far as I can tell without reading through 318 posts the difference is that in my version the links were hidden (display:none) while the cover was in place. If the cover was smaller you would have seen the links underneath.

With the cover in place there is no visual difference so the code isn’t really needed unless you made the cover smaller.

1 Like

Thank you so much for explaining that to me, now I completely understand.

Thanks again.

I would have never figured that out on my own.

2 Likes

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