How to add a basic JS Hook to respond to mouseover / mouseout events

Not sure what I am doing wrong here.

I am creating a show/hide effect based on adding/removing classes when the user hovers over an element.
I am using a JS hook to do this.

I wrote a simple spike test of what I want to do using JS fiddle.
I wrote the JS Fiddle code and it works as expected in regular JS
Here is the js fiddle: Edit fiddle - JSFiddle - Code Playground

When I set my JS Hook to reflect the JS Fiddle code, the mouseout effect does not work with my Phoenix code and the mouseover effect is unpredictable and does not individuate between some elements
I want to know how to fix this.

Here is my Phoenix Code.

HOOK

	InfoColumnHover: {
		    // https://jsfiddle.net/IrvinDominin/7K2Z3/
		    mounted() {

		        let infoButtons = document.getElementsByClassName("info_column");
		        let infoContents = document.getElementsByClassName("info_content");

		        for (let i = 0; i < infoButtons.length; i += 1) {

		            infoButtons[i].addEventListener("mouseover", () => {
		            	infoContents[i].classList.remove("info_content")
		                infoContents[i].classList.add("show-item")
		            });

		            infoButtons[i].addEventListener("mouseout", () => {
		            	 infoContents[i].classList.remove("show-item")
		                infoContents[i].classList.add("info_content")
		              
		            })

		        }

		    }
		},
	
	
 <button id={"info_column_parent#{testbed.id}"} phx-hook="InfoColumnHover" class="info_column">Info</button> 


                          <span>
                            <span id={"item_#{testbed.id}"} class="info_content">
                              <ul class="bg-[#fff8c1fc] text-black absolute p-5 outline outline-[#e11d48] outline-1 font-medium">
                                <li><b>Name:</b> <%= testbed.name %></li>
                                <hr>
                                <li><b>Manager:</b> <%= testbed.manager%></li>
                                <hr>
                                <li> <b>vNetC Auto Upgrade:</b> <%= testbed.vnetc_auto_upgrade %></li>
                                <hr>
                                <li><b>Firmware Auto Upgrade: </b><%= testbed.firmware_auto_upgrade %></li>
                                <hr>
                                  <li><b>Firmware Version: </b><%= testbed.firmware_version %></li>
                                <hr>
                                <li><b>Managed Out of Service: </b><%= testbed.managed_out_of_service %></li>
                                <hr>
                                 <li><b>DB Backup: </b><%= testbed.db_backup %></li>
                                 <hr>
                              </ul>
                            </span>
                          </span>

CSS

.info_content {
    display:none;
}


.show-item{
    display:block;
}

WHen you do phx-hook="InfoColumnHover" you will invoke mounted() for each and every button. In your mounted() you’re then traversing the whole DOM and adding your event listeners to every button. mounted() should only add event listeners to the actual button it was mounted to.

What is the correct way to go about creating this effect?

Initially I did a search for phx-mouseover as a “binding” but it isn’t part of the API

You could still try it this way you just need to bind to the item in mounted eg:

  mounted() {
    this.el.addEventListener(...

I tried to change my code to reflect your suggestion. My code is below. It does exactly the same thing.

	InfoColumnHover: {

		    mounted() {

		        let infoButtons = document.getElementsByClassName("info_column");
		        let infoContents = document.getElementsByClassName("info_content");

		        for (let i = 0; i < infoButtons.length; i += 1) {

		            this.el.addEventListener("mouseover", () => {
		            	infoContents[i].classList.remove("info_content")
		                infoContents[i].classList.add("show-item")
		            });

		            this.el.addEventListener("mouseout", () => {
		            	 infoContents[i].classList.remove("show-item")
		                infoContents[i].classList.add("info_content")
		              
		            })

		        }

		    }
		},

I take that back, it does a similar thing but the behavior is slightly different with overlapping elements that don’t respond to mouseout events.

BTW I tried doing this with “only CSS” with elements responding unpredictably between each other. I figured doing the JS way would be a more direct route until I read phx-mouseover doesn’t exist. So now I am trying to do it with hooks.

@wktdev Again the issue is that you are iterating over every button. This is what I mean:

		    mounted() {

		            this.el.addEventListener("mouseover", () => {
		            	infoContents[i].classList.remove("info_content")
		                infoContents[i].classList.add("show-item")
		            });
		            this.el.addEventListener("mouseout", () => {
		            	 infoContents[i].classList.remove("show-item")
		                infoContents[i].classList.add("info_content")
		              
		            })
		    }

That just gives me an error because this doesn’t exist:

infoContents[i]

Of course the code is not complete…

BTW I think You should use the hook only in the context of this.el

If You want to use something like this, You should put this code in app.js not in the hook.

Hooks are meant to be used inside a component, and not try to manipulate global DOM.

Yeah you’ll need to get the contents in the child. But basically you can’t do global modifications from a hook. You should set a hook on a dom element and then manipulate just that dom element and its children.

I’m curious what behavior you observed with a CSS solution - this fork of your Fiddle seems to do the same thing without any JS (in Chrome on OS X, if that makes a difference).

I tried to do it with tailwind. Your way works and is simpler.