How to trigger a href click event from the parent element

Viewed 2034

With below code I'm trying to click a tag click event from the parent li tag. But it gives me this error:

Why I want to do this:

When I click the PHP PDO link we need to cursor move in to that text not the li tag. I'm trying to fix it using this. But I know we can call that href from the li click event by getting the a href attribute and set it into the window.location.href. But still trying to trigger the a href click event when I click the li tag.

HTML

<li class="tags" style="cursor: pointer;">
    <a class="link_em" href="?l=1" id="1">List1</a>
</li>

Jquery:

$('li.tags').on('click', function (e) {
    $(this).children('a').click();
    return false;
});

Error:

I got this error when I use above code.

 Uncaught RangeError: Maximum call stack size exceeded

UI:

enter image description here

https://jsfiddle.net/k92dep45/

Edited:

I done this ugly thing, but I am still looking proper solution:

$('li.tags a').on('click', function (e) {
   e.stopPropagation();
});
$('li.tags').on('click', function (e) {
  window.location.href = $(this).children('a.link_em').attr('href');
  e.preventDefault();
});
4 Answers

You can use below version for the above code

$('li.tags').on('click', 'a', function (e) {
// Do your stuff.
e.preventDefault();
   window.location.href= thishref;
});

Here the second argument is the children 'a' in the click event of jQuery.

The problem is that you keep calling the same element which leads to the Maximum call stack size exceeded.

Look at the below example.

function foo(){
   foo();
}

foo();

In the above code, we are calling foo() again and again and it will also produce the same result.

You can do it differently by changing the href of the window to the href stored in your a tag, getted by using this instruction:

$(this).children('a').attr("href");

Try to implement, or run the following code snippet to confirm if its resolving your problem & get the expected render:

$('li.tags').on('click', function (e) {
    window.location.href= $(this).children('a').attr("href");
    return false;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<li class="tags" style="cursor: pointer;">
    <a class="link_em" href="?l=1" id="1">List1</a>
</li>

You need to add event.stopPropagation() on a element and prevent event "bubbling" otherwise you create infinite loop and that is why you get that error.

$('li.tags').on('click', function() {
  $(this).children('a').click();
});


$('li.tags a').click(function(e) {
  e.stopPropagation()
  e.preventDefault() // This is just for demo
  console.log($(this).text())
})
a {
  border: 1px solid black;
}
li {
  padding: 50px;
  border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
  <li class="tags" style="cursor: pointer;">
    <a class="link_em" href="?l=1" id="1">List1</a>
  </li>
</ul>

Edit

After rereading the question, I think that OP has over complicated things (as have I obviously). Added a simpler version of Demo 1 labeled Demo 2. And added Demo 3 which is solution that uses no JavaScript/jQuery just CSS.

Capture Phase

When you need fine grained control over the event chain .addEventListener() is better suited to handle particular aspects like firing the event on the capture phase instead of the bubbling phase. The reason why is because li.tag will be before a.link on the first phase (i.e. capture phase) of the event chain. We assign capture or bubbling phase by assigning the appropriate boolean value to the third parameter:

document.addEventListener('click', function(e) {...}, false);

This is default, which is set at false for the bubbling phase.

document.addEventListener('click', function(e) {...}, true);

This will make the registered object (e.g.document in this example) listen and act on the capture phase. The following demo -- during capture phase -- will:

  1. assign the clicked li.tag as the event.target...
  2. ...then invoke e.stopPropagation() so the a.link nested within the li.tag will not be included in the event chain. The bubbling phase is skipped as well.
  3. Instead, this a.link becomes e.target on a new event chain because the trigger method .click() is invoked on it.


Test

The 3rd link of List0 tests whether a.link is indeed triggered by .click() or firing during capture or bubbling phase, or is the event.target. Because each a.link in List0 has pointer-events:none which makes a.link oblivious to any mouse or click event from a user, we can conclude:

  1. that any activation of said a.link is entirely dependant upon its parent li.tag being clicked by a user.

  2. during the capture phase, no click event will reach a.link due to e.stopPropagation() and pointer-events:none it can't be e.target.

  3. bubbling phase isn't even there for a.link because of e.stopPropagation()

  4. As that event chain dies off .click() is fired and a.link goes off like a second stage rocket

When clicking anywhere on the 3rd list item we get these results:

  • log: Target: LI
  • Does not jump to List2

When clicking the first or second list item of List0 we get these results:

  • log: LinkID: {0-1 or 0-2}
  • log: Target: A
  • log: Target: LI
  • Jumps to List1 or List2


Errors

In the Fiddle, there were 2 instances of invalid HTML:

Error 1: <ui>

Correct: <ul>

Error 2: All <a> had the same id, all id must be unique

   <a class="link_em" href="?l=1" id="1">List1</a>

   <a class="link_em" href="?l=2" id="1">List2</a>

Correct:

   <a href='#l1-1' class='link' id='0-1'></a>

   <a href='#l1-2' class='link' id='0-2'></a>

You'll notice that the classes and ids in HTML are slightly different, but it'll be ok to change back to your own as long as you mind the errors mentioned above.


Demo 1

document.addEventListener('click', function(e) {
  if (e.target.className === 'tag') {
    e.stopPropagation();
    var link = e.target.querySelector('a');
    console.log('ID: ' + link.id);
    link.click();
    console.log('Target: ' + e.target.tagName);
    return false;
  } else {
    console.log('Target: ' + e.target.tagName);
    return false;
  }
}, true);
ul {
  height: 90px;
  outline: 1px solid red;
}

li {
  height: 30px;
  outline: 1px solid blue;
  cursor: pointer
}

.link {
  display: block;
  width: 10px;
  height: 10px;
  background: red;
  pointer-events: none;
  cursor: default;
  position: relative;
  z-index: 1;
}

b {
  float: left;
}

hr {
  margin-bottom: 1500px
}

.as-console {
  position: fixed;
  bottom: 30px;
  right: 0;
  max-width: 150px;
  color: blue;
}

.top {
  width: 100px;
  pointer-events: auto;
}
<h1>Capture Phase Demo</h1>
<details>
  <summary>Test</summary>
  <p>Third list item is not a .tag.</p>
  <dl>
    <dt>Results:</dt>
    <dd>Logged: "Target: LI"(or "Target: "B")</dd>
    <dd>Did not log: "ID:0-2"</dd>
    <dd>Did not jump to List2</dd>

    <dt>Conclusion:</dt>
    <dd>Links on List0 do not respond to mouse or click events from user. Therefore in order to jump from List0 the link must be triggered programmatically.</dd>
  </dl>
</details>

<h2 id='l1-0'>List0</h2>
<ul>
  <li class='tag'>
    <a href='#l1-1' class='link' id='0-1'>List1</a>
  </li>
  <li class='tag'>
    <a href='#l1-2' class='link' id='0-2'>List2</a>
  </li>
  <li>
    <a href='#l1-2' class='link' id='0-2'>Test</a>
  </li>
</ul>
<b>1500px to List1 </b>
<hr>
<h2 id='l1-1'>List1</h2>
<ul>
  <li><a class='top' href='#l1-0'>Back to Top</a></li>
  <li>Item</li>
</ul>

<b>1500px to List2 </b>
<hr>
<h2 id='l1-2'>List2</h2>
<ul>
  <li>
    <a class='top' href='#l1-0'>Back to Top</a>
  </li>
  <li>Item</li>
</ul>


Demo 2

document.addEventListener('click', function(e) {
  if (e.target.className === 'tag') {
    e.stopPropagation();
    e.target.querySelector('a').click();
  }
}, true);
ul {
  height: 100px;
  outline: 1px solid red
}

li {
  height: 100%;
  cursor:pointer;
}
<h1 id='list0'>List0</h1>
<ul>
  <li class='tag'>
    <a href='#list1'>List1</a>
  </li>
</ul>
<hr>
<h1 id='list1'>List1</h1>
<ul>
  <li class='tag'>
    <a href='#list0'>List0</a>
  </li>

Demo 3

ul {
  height: 100px;
  outline: 1px solid blue
}

li {
  height: 100%;
  padding: 0
}

a {
  display: block;
  height: 100%;
  width: 100%;
}
<h1 id='list0'>List0
  <h1>
    <ul>
      <li>
        <a href='#list1'>List1</a>
      </li>
    </ul>
    <hr>
    <h1 id='list1'>List1
      <h1>
        <ul>
          <li>
            <a href='#list0'>List0</a>
          </li>
        </ul>

Related