If I were to put 2 of these on a page, it would look like

I don’t think it can be done. If you have like 6 or so on one page, they will all interfere with each other. If there’s no way to distinguish one from another.

I don’t need your negativity to get this done, boy.

1 Like

It’s not negativity. It just is. If you have everything as the same name, it can’t possibly work like that, but I need help getting the other code working, which I don’t understand how to do, and which I’m painstakingly trying to figure out.

The complexity of managing even two of them might be getting in your way. So first up, move all of the CSS code over to the CSS panel, and move all of the JavaScript over to the JavaScript panel.

The steps that I plan to do are:

  • add unnumbered css classname definitions to the CSS area
  • for each numbered declaration, add to the HTML element the unnumbered stylename too
  • move common style declarations from the numbered styles over to the unnumbered styles

I won’t remove the numbered styles from the HTML code yet, because the JavaScript code relies on some of them too. So, updating the javascript code will occur in a follow up post. After that, the numbered styles can be removed.

Adding unnumbered CSS definitions

First I’m going to group together related CSS declarations, so that I’m not scrolling up and down all of the time.
I see that you have an unnumbered style, and a numbered style, which will make the conversion easier.

  .wrap {
    position: relative;
    display: table;
    white-space: nowrap;
    background: red;
  }
  .wrap1 {
    position: relative;
    display: table;
    white-space: nowrap;
    background: red;
  }

After grouping similar ones together, whenever I see an a numbered style that has the same declarations as an unnumbered style declaration, I can just add that style to the HTML code on the page.

With wrap1 for example, the HTML code becomes:

<div class="wrap wrap1">

Because those two style declarations are the same, I can remove the wrap1 style declaration from the CSS panel.

Can I remove wrap1 completely? Checking the JavaScript panel, I see that wrap1 is not being used there, so I can remove wrap1 from that HTML element.

<div class="wrap">

There is now no wrap1 classname anywhere in the HTML, the CSS, or the JavaScript code.
The class name of “wrap” is now working for the two codes. https://jsfiddle.net/Lrv7z33w/3/

The same process can be continued for other numbered class names, which I will continue with below.

What if they are totally 2 different types of codes that look totally different from each other?

And they both function differently.

We can get in to that after this current example is complete.

I’m working on the other code right now though. This can wait till that is done first. One thing at a time. After the players are all on one page, then that can be worked out at that point.

I’ve started the work on this, so I’m going to finish working on it.

The next CSS declaration to remove the numbered classname from is for “initial”

Adding “initial” to the HTML code, I can remove the .initial1 style declaration.

    <div class="initial initial1">Links </div>

And the code still works. It’s important to always check that things remain working.

Is “initial1” used in the JavaScript code? You betcha it is, so it doesn’t get removed from the HTML code yet.
Instead, work continues on removing the duplication from the CSS style declarations, and the JavaScript code can be worked on after that CSS task is complete.

To help simplify the testing, I’m moving the numbered HTML code down below the unnumbered HTML code.
That way the scripting will continue to work properly, without accidentally affecting the second one that we are working on. As always, it’s important that testing continue work properly, otherwise you don’t know when you’ve strayed off in to the weeds.

“link1” isn’t used by the JavaScript, and as the CSS declarations are the same, can be renamed to “link” with the .link1 declaration being removed. And testing shows that the code still works.

“hide” and “hide1” - that’'s just stupid. The .hide1 CSS declaration is immediately removed as nothing uses hide1

The CSS declaration for .link1 div:last-child is also removed, as link1 was changed to link earlier on too.

The CSS code now has no numbered style declarations, and everything continues to work. https://jsfiddle.net/Lrv7z33w/4/

The next thing to work on is the JavaScript, where we remove the reliance on the numbered class names too.
When JavaScript no longer uses the numbered class names, we can then remove the numbered class names from the HTML code too, and nothing will then use numbered class names.

1 Like

The first JavaScript numbered classname is an easy one to deal with.

    document.querySelector(".myLink1").classList.add("hide");

We can add “myLink” to the element that contains “mylink1”, and update the JavaScript so that it gets everything with “myLink” and works with those instead.

  <div class="myLink myLink1">

Do things still work when we run that? yes it done. Time to work on the JavaScript
We can use querySelectorAll to get all of the matching elements, and then loop through each of them.

    document.querySelectorAll(".myLink").forEach(function (myLink) {
        myLink.classList.add("hide");
    });

Where else is myLink1 used. It’s used inside the handler function (which needs to be cleaned up too, but that’s a different task for later).

    function playButtonClickHandler() {
      document.querySelector(".myLink1").classList.remove("hide");
      ...
    }

We can use a similar querySelectorAll technique here as before.

    function playButtonClickHandler() {
      document.querySelectorAll(".myLink").forEach(function (myLink) {
          myLink.classList.remove("hide");
      });
      ...
    }

Already we have two sets of duplicated code, and we are going to have more so now is a good time to do something about that duplication.

We need a function that accepts a selector, and a function, and runs that function on all things that match the selector. That function is normally called a callback function.

The function could be called, forEachEl(selector, callback)

    function forEachEl(selector, callback) {
      document.querySelectorAll(selector).forEach(callback);
    }

That now simplifies the other code that we had before:

    forEachEl(".myLink", function(el) {
      el.classList.add("hide");
    });
    ...
  forEach(".myLink", function(el) {
    el.classList.remove("hide");
  });

If we had a show and hide function, which we have done in other work, that would then become:

    forEachEl(".myLink", hide);
    ...
    forEachEl(".myLink", show);

Which can’t get much simpler than that.

But regardless of that, does the code still work after completely removing “link1”? https://jsfiddle.net/Lrv7z33w/5/ Yes it does.

Now to remove other numbered class names.

1 Like

“playButton1” is the next numbered classname to remove.

The JavaScript code starts off as:

      var button = document.querySelector(".playButton1");

We need to get the button that was clicked. We can’t do that yet, not until the JavaScript code adds an event listener on to all of the matching playButton elements, so it’s time to look at the addEventListener code instead.

    var playButton = document.querySelector(".playButton1");
    playButton.addEventListener("click", playButtonClickHandler);

We need the playButton function to do everything based on the button that was pressed, so some updates need to occur to that code.

First, we simplify things by moving most of the code out of the handler and to a separate togglePlayButton function.

    function togglePlayButton(button) {
      forEachEl(".myLink", function(el) {
        el.classList.remove("hide");
      });
      // var button = document.querySelector(".playButton1");
      var player = button.querySelector("audio");
      var play = button.querySelector(".play1").classList.add("hide");
      var pause = button.querySelector(".pause1").classList.add("hide");
    }
    function playButtonClickHandler() {
      var button = document.querySelector(".playButton1");
      togglePlayButton(button);
    }

That is quite a structural change to the code, so does it work? Yes it still works when starting the first player and then the second, but not when starting the second one first. Why would that be?

It’s the code to hide the myLink element. It shouldn’t do it for all classnames. We only want to remove hide from the one that’s by the button, not from all of them on the page.

      forEachEl(".myLink", function(el) {
        el.classList.remove("hide");
      });

As the myLink element comes just before the playButton one, we can use previousElementSibling to get that myLink element.
And for good measure, we should move the statement down below the variable declarations.

    function togglePlayButton(button) {
    // forEachEl(".myLink", function(el) {
    //   el.classList.add("hide");
    // });
      var player = button.querySelector("audio");
      var play = button.querySelector(".play1").classList.add("hide");
      var pause = button.querySelector(".pause1").classList.add("hide");
      button.previousElementSibling.classList.remove("hide");

The code now still works, and further progress can now be made towards removing the remaining numbered class names from the code. https://jsfiddle.net/Lrv7z33w/6/

Continuing the focus on ensuring that the togglPlayButton function relies only on the clicked button to retrieve what it needs, the code relating to “initial1” is the next target of our inspection.

      playButton.querySelector(".initial1").classList.add("hide");

Why does that work? It shouldn’t, because playButton hasn’t been defined inside of the togglePlayButton function.

It works because outside of the function below the handler function, we assigned a playButton variable there.

    function playButtonClickHandler() {
      ...
    }
    var playButton = document.querySelector(".playButton1");
    playButton.addEventListener("click", playButtonClickHandler);

If we ever decide to inline that playButton variable, things are going to break because as a side-effect of removing that playButton variable, the togglePlayButton will fail to work too. Side-effects are bad, and need to be avoided.

    // things go bad because the playButton variable has gone
    document.querySelector(".playButton1").addEventListener("click", playButtonClickHandler);

We can fix that problem by renaming playButton variable in the the togglePlayButton function, to button instead.

    function togglePlayButton(button) {
      ...
      // playButton.querySelector(".initial1").classList.add("hide");
      button.querySelector(".initial1").classList.add("hide");
      ...
        // playButton.querySelector(".pause1").classList.remove("hide");
        button.querySelector(".pause1").classList.remove("hide");
      ...
        // playButton.querySelector(".play1").classList.remove("hide");
        button.querySelector(".play1").classList.remove("hide");
      ...
    }

The code now works, even when there is no playButton variable. Now that we’ve confirmed that things with even without the playButton variable, we can return that event listener code back to how it was before.

    var playButton = document.querySelector(".playButton1");
    playButton.addEventListener("click", playButtonClickHandler);

I think that the togglePlayButton function now work entirely based on the button variable, so the event handler function can now be updated to pass in different buttons.

    function playButtonClickHandler() {
      var button = document.querySelector(".playButton1");
      togglePlayButton(button);
    }

This click handler needs to work no matter which button was clicked on. We can get that button from the this keyword.

    function playButtonClickHandler() {
      // var button = document.querySelector(".playButton1");
      var button = this;
      togglePlayButton(button);
    }

And now that the handler can handle any button, we can carry on to update the event listener code.

    var playButton = document.querySelector(".playButton1");
    playButton.addEventListener("click", playButtonClickHandler);

The event listener code needs to find all playButton elements instead, and assign the same click handler to each of them. With the forEachEl function, that’s now easy to achieve.

    // var playButton = document.querySelector(".playButton1");
    // playButton.addEventListener("click", playButtonClickHandler);
    forEachEl(".playButton", function (el) {
      el.addEventListener("click", playButtonClickHandler);
    });

Does the code still work? It does on the second set of links, but not on the first set.
That’s because the togglePlayButton function still uses numbered classnames.

Removing the numbered classnames so that they are unnumbered, allows the togglePlayButton function to work on both buttons.

    function togglePlayButton(button) {
      ...
      // var play = button.querySelector(".play1").classList.add("hide");
      var play = button.querySelector(".play1").classList.add("hide");
      // var pause = button.querySelector(".pause1").classList.add("hide");
      var pause = button.querySelector(".pause").classList.add("hide");
      ...
      // button.querySelector(".initial1").classList.add("hide");
      button.querySelector(".initial").classList.add("hide");
        ...
        // button.querySelector(".pause1").classList.remove("hide");
        button.querySelector(".pause").classList.remove("hide");
        ...
        // button.querySelector(".play1").classList.remove("hide");
        button.querySelector(".play").classList.remove("hide");
      ...
    }

Does it work? The first player is starting then stopping immediately.
Now that the togglePlayButton function works on both buttons, the second set of JavaScript code is no longer needed.

After deleting that second set of JavaScript code, everything works well. https://jsfiddle.net/Lrv7z33w/6/

There’s only one more thing to do now, and that is to remove the numbered class names from the HTML elements.

1 Like

As none of the CSS code and none of the JavaScript code has any numbered classnames anymore, we can remove them from the HTML code.

  <div class="playButton playButton1">
    <div class="initial initial1">Links </div>
    <svg class="pause pause1 hide" ...>
    ...
    <svg class="play play1 hide" ..>
    ...

Here’s what the HTML code looks like with the numbered class names removed.

  <div class="playButton">
    <div class="initial">Links </div>
    <svg class="pause hide" ...>
    ...
    <svg class="play hide" ..>
    ...

And the code now works perfectly well with no numbered class names. https://jsfiddle.net/Lrv7z33w/7/

Can we fix it? Yes we can!

2 Likes

This looked like a lot of work, and it’s all because you have been failing to use techniques that allow a more flexible approach to be taken to the CSS and JavaScript code.

Frequently while working on only one piece of code, it’s easy to become blinkered and fail to see the larger picture. Good code practices allow you to work on single individual pieces, and at the same time make it painless to handle multiple items too.

You would do well to listen to experts when they attempt to educate you about such good practices. They are there for a good reason, which is to help reduce the pain of future development.

4 Likes

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