Babr
Babr

Reputation: 2081

How to get data attribute of event target in svelte?

I'd like to get the value of data-url in:

  {#each profiles as p}
    <div data-url={p.url} class="item" on:click={goToPage}>
      <div class="row">
        <div class="col s12 l4">
          <div class="a">
             <br />
          </div>
        </div>
        <div class="col s12 l8">
          <p>
            <strong>{p.fn} {p.ln}</strong><br />
            {p.ttl}
          </p>
        </div>
      </div>
      {p.dsc}
       <hr />
    </div>
  {/each}

The function is:

  const goToPage = (e) => {
     var slug = e.target.querySelector("item").getAttribute("url");
    console.log("slug is:", slug);
    window.location.href = slug;
  };

However it does not work and I get

Uncaught TypeError: Cannot read properties of null (reading 'getAttribute')

I have tried other things like

e.target.querySelector("item").getAttribute("data-url");

and

e.target.getAttribute("data-url");

but none of them worked.

Upvotes: 3

Views: 6564

Answers (3)

Taitu-lism
Taitu-lism

Reputation: 946

The accepted answer (by @stephane-vanraes) suggests a better approach then the one from the question - no need to query the DOM when svelte provides you with a way to pass data.


That said, let's try to explain why the question's code didn't work.

The problem lies in the query part, it returns null.
As the error points out, you end up with null.getAttribute().

So first, when querying by classname you should add a dot before the classname, i.e. .item:

.querySelector(".item") - with a dot

But this won't work either because of a more fundamental issue: You are looking for an item inside itself.

A.querySelector(B) looks for "B" inside of "A".

Now, depending on the clicked element, e.target could be the item itself or one of its children, so e.target.querySelector('.item') is either looking for something it already has or looking for a parent within one of its children.

Switch to e.currentTarget, as @TheBritishAreComing suggested, which always points to the element that listens to the event (the item) and skip the query.

Tip: When you finally have the item element you can access its data attributes with:
itemElm.dataset.url
which is much nicer than:
itemElm.getAttribute('data-url').

Either way, the right approach is as mentioned in the accepted answer (+ removing the data-url attribute as it's not needed with this approach).

Upvotes: 0

Stephane Vanraes
Stephane Vanraes

Reputation: 16451

The simplest, most straightforward way is to simply pass the slug as an argument to the function instead:

<div data-url={p.url} class="item" on:click={() => goToPage(p.url)}>

and the function becomes:

const goToPage = (slug) => {
  console.log("slug is:", slug);
  window.location.href = slug;
};

Upvotes: 5

TheBritishAreComing
TheBritishAreComing

Reputation: 1727

e.target references the thing you just clicked, you'll need to use currentTarget instead and you don't need to do querySelector

EG:

var slug = e.currentTarget.getAttribute("data-url");

I've done a basic example

function goToPage(e){
  console.debug(e.currentTarget.getAttribute('data-url'));
}
<div data-url="https://google.com" class="item" onClick="goToPage(event)">
      <div class="row">
        <div class="col s12 l4">
          <div class="a">
             <br />
          </div>
        </div>
        <div class="col s12 l8">
          <p>
            <strong>{p.fn} {p.ln}</strong><br />
            {p.ttl}
          </p>
        </div>
      </div>
      {p.dsc}
       <hr />
    </div>
    
    <div data-url="https://google2.com" class="item" onClick="goToPage(event)">
      <div class="row">
        <div class="col s12 l4">
          <div class="a">
             <br />
          </div>
        </div>
        <div class="col s12 l8">
          <p>
            <strong>{p.fn} {p.ln}</strong><br />
            {p.ttl}
          </p>
        </div>
      </div>
      {p.dsc}
       <hr />
    </div>

Upvotes: 5

Related Questions