Problems with deference in Functional Style

I have been working on two main themes of logic. But, I have been unable to implement a more sane solution, due to my incapability to defer the evaluation of some parts of the code.

This is one of the bits. This logic repeats itself for three different layouts. It’s a “click me and change border” functionality.

function toggleUp(){
    buyImagesDrink[0].classList.remove("buyDrink");
    showDrink[0].classList.remove("showDrink");
}
function toggleDown(){
    this.classList.add("buyDrink");
    this.getElementsByClassName("animation-hidden")[0].classList.add("showDrink");
}

function toggleThumbnail(){
    if (buyImagesDrink.length > 0){
	toggleUp();
    }
    toggleDown();
 }

for (var j=0; j < selectedDrink.length; j++){
    selectedDrink[j].addEventListener("click", toggleThumbnail());
};

Now, there is this other piece, which get’s activated (footer), when these three thumbails are active.

let combinedVar = buyImages.length + buyImagesDrink.length + buyImagesDessert.length;

// foot style changer function.
function styleChange() {
    if (combinedVar === 3){
	footerStyle[0].classList.add("order");
	footerStyleSpan.innerHTML = "Fechar pedido";
    }
};

//add event listener in all elements, so to make style change be independent of order of layout selection.
for (var m=0; m < selectedDessert.length; m++){
    selectedDessert[m].addEventListener("click", styleChange());
    selected[m].addEventListener("click", styleChange());
    selectedDrink[m].addEventListener("click", styleChange());
};

Demo running: https://jsfiddle.net/BuddhiLW/q3mwe9ky/13/

Can you be a little more explicit about the expected behavior and what it is that isn’t working as expected? I also do not see the code you posted here anywhere in the jsfiddle?


addEventListener just takes the identifier of the callback function it should run when the event triggers. If you pass it a function invocation the function will just run.

Example:

selectedDrink[j].addEventListener("click", toggleThumbnail());

Should be:

selectedDrink[j].addEventListener("click", toggleThumbnail);
1 Like

The ideia is that the application is running on fiddle. But, that is my imperative, very verbose and repetitive code. In contrast, I have posted what I’m trying to achieve.

The expected behavior is the application.

For example, in fiddle we have:

for (var k = 0; k < selectedDessert.length; k++) {
  // Variable responsible to count selected items in one of the rolls
  selectedDessert[k].addEventListener("click", function() {
    if (buyImagesDessert.length > 0) {
      buyImagesDessert[0].classList.remove("buyDessert");
      showDessert[0].classList.remove("showDessert");
    }
    this.classList.add("buyDessert");
    this.getElementsByClassName("animation-hidden")[0].classList.add("showDessert");
  });
};

What this piece is doing is two fold; it toggles a hidden item, giving it property “showDessert” (the little ‘right’ sign which appears when you click an image). And, it’s toggling the “buyDessert” which is the green border, when selected.

I would like to first, decouple these behavior in little functions.

function toggleDown(){
   buyImagesDessert[0].classList.remove("buyDessert");
   showDessert[0].classList.remove("showDessert");
}
function toggleUp(){
   this.classList.add("buyDessert");
   this.getElementsByClassName("animation-hidden")[0].classList.add("showDessert");
}

Finally, writting a third more general function,

function toggleThumbnail(){
    if (buyImagesDessert.length > 0){
	toggleDown();
    }
    toggleUp();
 }

Finally, I loop over this higher order function:

for (var j=0; j < selectedDessert.length; j++){
    selectedDessert[j].addEventListener("click", toggleThumbnail());
};

The error I get with this refactoring:
In this line,

this.classList.add("buyDessert");

log:

Uncaught TypeError: Cannot read property 'add' of undefined
    at toggleDown (highlight-buys-dessert.js:10)
    at HTMLLIElement.<anonymous> (highlight-buys.js:21)
  1. How do I pass a variable that can have defered-evaluation in ,addEventListener?
    i.e., the compiler is trying to evaluate the function, before it’s actually used in the for look. where there is the actual .addEventListener in action. Therefore, It is returned as a undefined element.

Also, I already corrected what you instructed me, on passing function with “()” to .addEventListener. But, now Apparently, all the rest of the things being equal, it simply do not highlight my footer anymore, at the same time, it doesn’t throw me an error.

Sorry for the late reply.

OK, I get it, you want to refactor the code you have now. I would suggest you post a jsfiddle with your current refactor you are working on.

I’m not sure I understand why you are repeating some of the things you are, including CSS. Does the elements not get the exact same CSS applied on click? You give the card a green border and you show an icon/image.


One option I came up with is this.

  1. Inside the click handler get the child elements of the parent of the element that was clicked (this.parentElement.children).

  2. Loop the children and remove classes as needed.

  3. Add the classes to the element that was clicked (this) and whatever child element (like icons).

  4. Check the length of the NodeList returned from querySelectorAll and toggle/change what is needed on a finished selection.

I was kind of bored so I made an example you can look at.

https://codepen.io/lasjorg/pen/MWJmWrY

1 Like

Holly smokes. Godness.

Thank you!

Functional style is so beautiful.

1 Like

Happy to help. If you have any questions about the code just ask.

I would like to share another application I developed with the use of your code. If you would like to see. I tried to be as much functional as I could. I think I did a reasonable job.