DEV Community

Cover image for Javascript listeners click bait
Love Liberty
Love Liberty

Posted on • Edited on

Javascript listeners click bait

Having written code in a test file that worked very nicely, I had to incorporate it into the main code & it didn't go so well.

The basic code consisted of a simple html file with a single button called "remember this."

Image description

That had an onclick to a function called showRememberMenu(). That function created a menu that was displayed under the button.

Image description

The dropdown menu had several tags to add to the data as a reminder of why the user was trying to remember it. (I'm old I need all the hlp I can get on remembering stuff.)

Click the button, see the dropdown menu, click the tag you want & the data you are looking at is saved in an array with that tag. Having clicked the menu it disappears.

My task was then to drop that into a larger script & see it work just the same... oh, maybe not. Click the menu, store the data with the tag and... what? why is the menu still there? It was deleted in the original as soon as it was clicked. Why is it not going away?

And if I click the tag that says 'Exit don't save' it is still displaying the menu.

As a user I was looking at a card that had data from the database. Clicking this button was a way to save that data for later use. Here you can see the card with its new button.

Image description

Click that button to see the drop down and select the tag that is relevant to remembering this data...

Image description

Now go do the same with other cards and you have all the data needed to complete some task, but why the "&%!!! is that menu never going away?

After I ran out of ideas I contacted my collegaue Gemini the AI. It gave me a first solution which was exactly the code that I already had. It suggested a lot of things, none of which worked.

It was confident that checking for the existence of the nodes & elements would make everything good, but it didn't.

It took about an hour of being told things that didn't work and being lectured on timing issues, possibly needing delays, which really weren't relevant, but slowly as a team we solved the problem.

If you want to try to solve this, I bet you can do it in less time.

Here's the original draft code that works...

<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="UTF-8">
  <title>Some Javascript</title>
  <link rel="stylesheet" href="./someJavascript.css">
  <link rel="icon" href="favicons/favicon.ico" type="image/icon type">
</head>
<body>
  <div class="remember" id="remember">
    <input type="button" class="remember_button" id=remember value="remember this" onclick="showRememberMenu()">
  </div>
      <script  src="./someJavascriptCode.js"></script>    
    </body>
    </html>
Enter fullscreen mode Exit fullscreen mode

and the javascript

let logToConsole=true;
var rowData={THId:'2', TaskName:'To climb a tree', TaskDesc:'Try using hands & feet'};
var remember=[];

function rememberProcess(remember){
    if(logToConsole) console.log('RememberProcess()');
for(i=0;i<remember.length;i+=2){
    console.log('Item ',i/2+1,' of ', remember.length/2,' items', 'Remember as ',remember[i], remember[i+1]);}        
}

function putDataIntoRemember(menuHeader,rowData){
    if(logToConsole) console.log('putDataIntoRemember()');
    remember.push(menuHeader);
    remember.push(rowData);
}

function deleteMenu(menuLu){
    if(logToConsole) console.log('deleteMenu()');
    menuLu.parentNode.removeChild(menuLu);
}

function showRememberMenu(){
    if(logToConsole) console.log('showRememberMenu()');

 if(document.querySelector('#rememberMenu') ) return; //menu already exists

    const remember_button = document.querySelector('#remember');
    const menuHeaders=['EXIT no save', 'as Student', 'as Manager', 'as Author', 'as Task' , 'as Note'];
    menuLu=document.createElement('lu');
    menuLu.id='rememberMenu';

for(let i=0;i<menuHeaders.length;i++){
    li=document.createElement('li');         
    li.innerText=menuHeaders[i]; 
    li.id=menuHeaders[i];
    li.classList.add('rememberLi');

    li.addEventListener('click', () => { //console.log('li button clicked', menuHeaders[i], rowData)
        if(menuHeaders[i]=='EXIT no save') {deleteMenu(menuLu);return};
    putDataIntoRemember(menuHeaders[i], rowData);//[header][rowData]       
    rememberProcess(remember);//do something with the stored data structure
    deleteMenu(menuLu);
      })

    menuLu.appendChild(li);
    }

remember_button.appendChild(menuLu);      
}
Enter fullscreen mode Exit fullscreen mode

There is some css, but it isn't relevant to the problem

.remember_button {

        background-color: #5A5050;
        border: 0;
        border-radius: 5px;
        box-shadow: 0 10px 10px rgba(0, 0, 0, 0.2);
        color: #fff;
        font-size: 12px;
        padding: 3px 4px;
        position: relative;        
        letter-spacing: 1px;
        width: 100px;

}

    /*Button goes white on hover*/
.remember_button:hover {
        background-color: #ffffff;
        color: #001F61;
        cursor:pointer;
    }    


.rememberLi{
    background-color: #aa9595;
    border: 0;
    border-radius: 5px;
    box-shadow: 0 10px 10px rgba(0, 0, 0, 0.2);
    color: #fff;
    font-size: 12px;
    padding: 3px 4px;
    position: relative;        
    letter-spacing: 1px;
    width: 100px;

}

.rememberLi:hover {
    background-color: #ffffff;
    color: #001F61;
    cursor:pointer;
}   
Enter fullscreen mode Exit fullscreen mode

The above works, more or less as I wanted.

Here is the version inside a bigger system which I won't post all of.

A card is dynamically produced and has the following code added to produce the same kind of button as in the original

                     // NEW  REMEMBER BUTTON   21:39 16 Nov 2024
  let cardRemember=document.createElement('button');
  cardRemember.classList.add='remember_button';
  cardRemember.id='remember';
  cardRemember.innerText='remember this';

  cardRemember.addEventListener('click', () => {
    showRememberMenu(rowData);
  });
  cardDivj.appendChild(cardRemember);
Enter fullscreen mode Exit fullscreen mode

and the rest of the javascript is close to identical to the original

var remember=[];

function rememberProcess(remember){
    if(logToConsole) console.log('RememberProcess()');
for(i=0;i<remember.length;i+=2){
    console.log('Item ',i/2+1,' of ', remember.length/2,' items', 'Remember as ',remember[i], remember[i+1]);}        
}

function putDataIntoRemember(menuHeader,rowData){
    if(logToConsole) console.log('putDataIntoRemember()');
    remember.push(menuHeader);
    remember.push(rowData);
}
/*
function deleteMenu(menuLu){
    if(logToConsole) console.log('deleteMenu()');
    console.log(menuLu);
    menuLu.parentNode.removeChild(menuLu);
}
    */

function deleteMenu(menuLu) {
  if (menuLu && menuLu.parentNode) {
    menuLu.parentNode.removeChild(menuLu);
    console.log("Menu removed successfully");
  } else {
    console.error("Menu or its parent not found");
  }
}

function showRememberMenu(rowData){
    if(logToConsole) console.log('showRememberMenu()');

 if(document.querySelector('#rememberMenu') ) return; //menu already exists

    const remember_button = document.querySelector('#remember');
    const menuHeaders=['EXIT no save', 'as Student', 'as Manager', 'as Author', 'as Task' , 'as Note'];
    menuLu=document.createElement('lu');
    menuLu.id='rememberMenu';

for(let i=0;i<menuHeaders.length;i++){
    li=document.createElement('li');               //  console.log(menuHeaders[i]);//ok
    li.innerText=menuHeaders[i]; // li.innerHTML = `Remember as ${menuHeaders[i]}`; //Gemini suggests this syntax 
    li.id=menuHeaders[i];
    li.classList.add('rememberLi');

    li.addEventListener('click', () => { console.log('li button clicked', menuHeaders[i], rowData)
        if(menuHeaders[i]==='EXIT no save') {deleteMenu(menuLu);return};
    putDataIntoRemember(menuHeaders[i], rowData);//[header][rowData]       
    rememberProcess(remember);//do something with the stored data structure
    deleteMenu(menuLu);
      })

    menuLu.appendChild(li);
    }

remember_button.appendChild(menuLu);      
}

Enter fullscreen mode Exit fullscreen mode

A reminder of what it looks like before being clicked
Image description

and how it get stuck after being clicked

Image description

I had no idea what the problem was, and Gemini was off on one of its lectures over entirely irrelevant stuff but Ai & I finally narrowed things down and now it seems soo simple.

So why does the menu persist? A clue from Shakespeare,

double double, toil and trouble
Fire burn and cauldron bubble

When the button was in html the menu went away as the javascript demanded, but when the button was in javascript it remained.

The problem I learnt is called 'bubbling up' where a click on the submenu is also detected as a click on the button. So the javascript button reacted by re creating the sub menu.

My AI colleague didn't spot the problem.

Top comments (0)