Search code examples
javascriptjqueryfocus

Handling keyboard focus tabbing on dynamically populated content


I have a list of blog posts with a load more button. For accessibility, I would like to change the positioning of keyboard focus once new posts are added so that instead of skipping to the elements below the post list, hitting tab after loading more posts will focus the first post in the new posts that were added in to the list.

My thought was to get the last article in the list, when the load more button is pressed, and then focus the link within it after the additional posts are loaded in. That way when the user clicks tab again, it will move to the next post in the list (the fourth post in my example). This is not working, I'm assuming because the entire HTML of the post container is being replaced. Is there a way to go about this considering all of the HTML is populated dynamically every time the load more button is pressed?

let additionalPosts = `
  <article>
    <a href="#">
      <h3>Fourth Post</h3>
      <p>Lorem ipsum dolor sit amet</p>
    </a>
  </article>
  <article>
    <a href="#">
      <h3>Fifth Post</h3>
      <p>Lorem ipsum dolor sit amet</p>
    </a>
  </article>
  <article>
    <a href="#">
      <h3>Sixth Post</h3>
      <p>Lorem ipsum dolor sit amet</p>
    </a>
  </article>
`;


$(function() {
  $('.load-more').click(function() {
    const $lastPost = $('.post-container article').last();
    //console.log($lastPost);
    $('.post-container').html($('.post-container').html() + additionalPosts);
    $('a', $lastPost).focus();
    $(this).hide();
  });
});
.post-container {
  display:flex;
  gap:1.5rem;
  flex-wrap:wrap;
  margin-bottom:3rem;
}

article {
  max-width:33%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<h1>Post List</h1>
<div class="post-container">
  <article>
    <a href="#">
      <h3>First Post</h3>
      <p>Lorem ipsum dolor sit amet</p>
    </a>
  </article>
  <article>
    <a href="#">
      <h3>Second Post</h3>
      <p>Lorem ipsum dolor sit amet</p>
    </a>
  </article>
  <article>
    <a href="#">
      <h3>Third Post</h3>
      <p>Lorem ipsum dolor sit amet</p>
    </a>
  </article>
</div>

<button class="load-more" href="#">Load More</button>

<div>
  <h3>Some additional links</h3>
  <a href="#">Another Link</a>
  <a href="#">Another Link</a>
  <a href="#">Another Link</a>
</div>


Solution

  • Minor changes will make this work as you were expecting.

    let additionalPosts = `
      <article>
        <a href="#">
          <h3>Fourth Post</h3>
          <p>Lorem ipsum dolor sit amet</p>
        </a>
      </article>
      <article>
        <a href="#">
          <h3>Fifth Post</h3>
          <p>Lorem ipsum dolor sit amet</p>
        </a>
      </article>
      <article>
        <a href="#">
          <h3>Sixth Post</h3>
          <p>Lorem ipsum dolor sit amet</p>
        </a>
      </article>
    `;
    
    
    $(function() {
      $('.load-more').click(function() {
        var $lastPost = $('.post-container article').last();
        $('.post-container').append(additionalPosts);
        $(this).hide().blur();
        $lastPost.find("a").trigger("focus");
      });
      $(".post-container").on("focus", "a", function() {
        console.log("Focus: " + $(this).closest("article").index());
      });
    });
    .post-container {
      display: flex;
      gap: 1.5rem;
      flex-wrap: wrap;
      margin-bottom: 3rem;
    }
    
    article {
      max-width: 33%;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <h1>Post List</h1>
    <div class="post-container">
      <article>
        <a href="#">
          <h3>First Post</h3>
          <p>Lorem ipsum dolor sit amet</p>
        </a>
      </article>
      <article>
        <a href="#">
          <h3>Second Post</h3>
          <p>Lorem ipsum dolor sit amet</p>
        </a>
      </article>
      <article>
        <a href="#">
          <h3>Third Post</h3>
          <p>Lorem ipsum dolor sit amet</p>
        </a>
      </article>
    </div>
    
    <button class="load-more" href="#">Load More</button>
    
    <div>
      <h3>Some additional links</h3>
      <a href="#">Another Link</a>
      <a href="#">Another Link</a>
      <a href="#">Another Link</a>
    </div>