Search code examples
javascripthtmlsvgclickaddeventlistener

Getting data on click on button with svg path


I'm trying to do buttons like the upvote buttons here, a button tag, with svg and path inside.
The appearance is ok, no problems with that. The problem is that the path tag has an attribute that I want to get when I click the button, I'm not very good at this, so to get the attribute that I want I used:
e.target.children[0].children[0].attributes.
And it gets to an error that I know the reason: the element has no children nor attributes, the error depends on where I click, if I click the path, the error is that it's child doesn't have a child (cause path's child is undefined), and if I click the svg, the problem is that the svg's child's child doesn't have attributes (again because it's undefined). So to don't get those errors, I have to click in the button, where there isn't path nor svg.
What should I do to being able to click in any pixel of the button and getting the same answer? I don't even know why it detects that I'm clicking on the svg or path even when those elements haven't any event listener.

When I started doing those buttons, I didn't use the button tag, it was just svg, and the eventListener was in the path element, the problem is that with small paths, is hard to click, so I started trying to use the click listener in the svg and now I put all inside a button tag and tried using the listener in the button, and I got the errors that I mentioned.

My apologies for not posting the code, it's a little long and horrible.

Edit:
I tried to take the most important of the code:

<body>
<div id="buttonCont">
</div>
<script>
    //Function that adds around 40 buttons.
    function addButtons(){
        buttonCont = document.getElementById("buttonCont");
        i = 0;
        while (paths(i) != ``) {
            buttonCont.innerHTML += `
            <button class="exButton">
                <svg class="exSVG">
                    <path class="exPath" pathNumber="${i}" d=""/>
                </svg>
            </button>`;
            i++;
        }
    }

    function on_load(){
        addButtons();
        exButtons = document.getElementsByClassName("exButton");
        for(let i = 0; i < exButtons.length; i++){
            exButtons[i].addEventListener("click", getNumber, false);
        }
    }
    
    //This is the function with the problem.
    function getNumber(e){
        pNumber = e.target.children[0].children[0].attributes.pathNumber.nodeValue;
        console.log(pNumber);
    }
    window.addEventListener("load", on_load, false);
</script>

After some hours I noticed that e.target returns the element that I clicked, not the element that has the listener, so anything inside the button is a different target. I'm looking a solution in javascript that allows me to get the button element with the click eventListener.


Solution

  • Passing in this with your listener will always ensure your starting point is the same. For example:

    <div onClick="getKey(this)">
      <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
        <path 
        data-key="12345"
        d="M 10,30
                 A 0,10 0,0,1 30,40
                 A 0,10 0,0,1 90,40"/>
      </svg>
    </div>
    
    <script>
    function getKey(button) {
     let key = button.querySelectorAll('path')[0].dataset.key;
     alert('key is ' + key)
    }
    </script>
    
    

    In the above code, getKey() will always receive the div element so that you can reliably know how many children deep you need to dig.

    Additionally, you can use button.querySelectorAll('path')[0] to isolate the path tag within the button so you don't have to cascade down the children properties.

    Here's a working JSFiddle: https://jsfiddle.net/sLp2qtde/1/