Arrow navigating through dropdown triggers site scroll

I got a custom dropdown with up and down key navigation. The issue is that when I use the keys the body scrolls at the same time as my items are selected. Any ideas?

$(document).on('click', '.js-click', function(e) {
  const $this = $(this).parent('.display');
  const $itemsContainer = $this.find('.display__items');

  if ($itemsContainer.hasClass('is-open')) {
    $itemsContainer.removeClass('is-open');
  } else {
    $itemsContainer.addClass('is-open').focus();
  }
});


$(document).on("keyup", ".display__items.is-open", function(e) {
  const $container = $(this);

  if (e.keyCode == 40) {
    moveElements($container, +1);
  }

  if (e.keyCode == 38) {
    moveElements($container, -1);
  }
});

function moveElements($container, position) {
  const $items = $container.find('.display__item');
  const $selected = $container.find('.selected');
  let index = $items.index($selected);

  index += position;

  if (index > 5) {
    index = 0;
  }

  if (index < -1) {
    index = $items.length - 1;
  }

  $items.removeClass('selected').eq(index).addClass('selected');
}
* {
  box-sizing: border-box;
}

body {
  min-height: 100rem;
}

.display {
  background-color: #ededed;
  padding: 1rem;
}

.display__items {
  display: none;
}

.display__items.is-open {
  display: block;
  outline: 0;
}

.display__item {
  padding: .4rem;
  background-color: #ffffff;
  margin-bottom: .25rem;
}

.selected {
  background-color: lightblue;
}

.display__input {
  padding: .25rem;
  width: 100%;
  margin-bottom: 1rem;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="display">
  <div class="display__input js-click">click</div>

  <div class="display__items" tabindex="-1">
    <div class="display__item">1</div>
    <div class="display__item disabled">2</div>
    <div class="display__item selected">3</div>
    <div class="display__item">4</div>
    <div class="display__item">5</div>
    <div class="display__item">6</div>
  </div>
</div>

Answer

Try adding a keydown listener as well that calls preventDefault when the target is .display__items.is-open, to prevent the browser’s default action of scrolling the page when the up or down arrow is pressed:

$(document).on("keydown", ".display__items.is-open", (e) => {
  e.preventDefault();
});

$(document).on('click', '.js-click', function(e) {
  const $this = $(this).parent('.display');
  const $itemsContainer = $this.find('.display__items');

  if ($itemsContainer.hasClass('is-open')) {
    $itemsContainer.removeClass('is-open');
  } else {
    $itemsContainer.addClass('is-open').focus();
  }
});


$(document).on("keyup", ".display__items.is-open", function(e) {
  const $container = $(this);

  if (e.keyCode == 40) {
    moveElements($container, +1);
  }

  if (e.keyCode == 38) {
    moveElements($container, -1);
  }

  if (e.keyCode == 13) {
    console.log('enter');
  }
});

function moveElements($container, position) {
  const $items = $container.find('.display__item');
  const $selected = $container.find('.selected');
  let index = $items.index($selected);

  index += position;

  if (index > 5) {
    index = 0;
  }

  if (index < -1) {
    index = $items.length - 1;
  }

  $items.removeClass('selected').eq(index).addClass('selected');
}

const moveElement = function(position) {
  const $items = $('.display__item');
  const $selected = $('.display__item--selected');
  let index = $items.index($selected);

  index += position;

  if (index > 5) {
    index = 0;
  }

  if (index < -1) {
    index = $items.length - 1;
  }

  $items.removeClass('display__item--selected').eq(index).addClass('display__item--selected');
}
* {
  box-sizing: border-box;
}

body {
  min-height: 100rem;
}

.display {
  background-color: #ededed;
  padding: 1rem;
}

.display__items {
  display: none;
}

.display__items.is-open {
  display: block;
  outline: 0;
}

.display__item {
  padding: .4rem;
  background-color: #ffffff;
  margin-bottom: .25rem;
}

.selected {
  background-color: lightblue;
}

.display__input {
  padding: .25rem;
  width: 100%;
  margin-bottom: 1rem;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="display">
  <div class="display__input js-click">click</div>

  <div class="display__items" tabindex="-1">
    <div class="display__item">1</div>
    <div class="display__item disabled">2</div>
    <div class="display__item selected">3</div>
    <div class="display__item">4</div>
    <div class="display__item">5</div>
    <div class="display__item">6</div>
  </div>
</div>

Leave a Reply

Your email address will not be published. Required fields are marked *