最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Get the element from above or below - Stack Overflow

programmeradmin2浏览0评论

Is there a way to get the element that is visually underneath or above a certain element?

The elements are part of a list, and I want to be able to navigate through the list using the arrow keys. Left/right moves to the previous/next sibling, but I have no idea what to do with up/down.

$(document).on('keydown', function(e) {
  console.log(e.which);

  var current = $('li.selected');

  switch (e.which) {
    case 37:
      current.prev().addClass('selected').siblings().removeClass('selected');
      break;

    case 39:
      current.next().addClass('selected').siblings().removeClass('selected');
      break;

    default:
      return true;
  }

  e.preventDefault();
});

$('li').on('click', function(e) {
  $(this).addClass('selected').siblings().removeClass('selected');
});
ul {
  display: flex;
  flex-wrap: wrap;
  list-style: none;
}
li {
  width: 50px;
  height: 50px;
  margin: 5px;
  border: 1px solid red;
}
li.selected {
  background: red;
}
<script src=".1.1/jquery.min.js"></script>
<ul>
  <li class="selected"></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
</ul>

Is there a way to get the element that is visually underneath or above a certain element?

The elements are part of a list, and I want to be able to navigate through the list using the arrow keys. Left/right moves to the previous/next sibling, but I have no idea what to do with up/down.

$(document).on('keydown', function(e) {
  console.log(e.which);

  var current = $('li.selected');

  switch (e.which) {
    case 37:
      current.prev().addClass('selected').siblings().removeClass('selected');
      break;

    case 39:
      current.next().addClass('selected').siblings().removeClass('selected');
      break;

    default:
      return true;
  }

  e.preventDefault();
});

$('li').on('click', function(e) {
  $(this).addClass('selected').siblings().removeClass('selected');
});
ul {
  display: flex;
  flex-wrap: wrap;
  list-style: none;
}
li {
  width: 50px;
  height: 50px;
  margin: 5px;
  border: 1px solid red;
}
li.selected {
  background: red;
}
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
  <li class="selected"></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
</ul>

Share Improve this question edited Jan 16, 2015 at 1:38 Mr. Polywhirl 48.9k12 gold badges94 silver badges145 bronze badges asked Jan 16, 2015 at 1:36 nice assnice ass 16.7k8 gold badges53 silver badges92 bronze badges 4
  • you can read elements' positions and find the one to navigate to. – akonsu Commented Jan 16, 2015 at 1:41
  • 1 I would remend using a <table> instead. Then you can navigate the rows and columns. – Ian Hazzard Commented Jan 16, 2015 at 1:41
  • No, he means that if the div flows, can you detect a neighboring div. (Think line wrapping) – Mr. Polywhirl Commented Jan 16, 2015 at 1:41
  • I want to avoid tables, because the number of columns is variable (as much as they can fit). But heights should be equal like in tables, that's why I chose flex... – nice ass Commented Jan 16, 2015 at 1:45
Add a ment  | 

3 Answers 3

Reset to default 5

I wrote a function to search sibling nodes and check their left position within the bounds of the element width.

function findDown(node) {
  return findInSiblings(node, node.nextAll());
}

function findUp(node) {
  return findInSiblings(node, node.prevAll());
}

function findInSiblings(node, siblings) {
  var x = node.offset().left,
    w = node.width() / 2;
  for (var i = 0; i < siblings.length; i++) {
    var sibling = $(siblings[i]);
    if (sibling[0].nodeType === 1) {
      var offset = sibling.offset().left;
      if (offset - w < x && offset + w > x) {
        return sibling;
      }
    }
  }
  return null;
}

Demo

$(document).on('keydown', function(e) {
  var current = $('li.selected');
  var node = getNode(current, e.which);
  if (node == null) {
    return true;
  }
  select(node);
  e.preventDefault();
});

$('li').on('click', function(e) {
  select($(this));
});

function select(node) {
  node.addClass('selected').siblings().removeClass('selected');
}

function getNode(selected, direction) {
  switch (direction) {
    case 37: // Left
      return selected.prev();
    case 38: // Up
      return findUp(selected);
    case 39: // Right
      return selected.next();
    case 40: // Down
      return findDown(selected);
  }
  return null;
}

function findDown(node) {
  return findInSiblings(node, node.nextAll());
}

function findUp(node) {
  return findInSiblings(node, node.prevAll());
}

function findInSiblings(node, siblings) {
  var x = node.offset().left,
    w = node.width() / 2;
  for (var i = 0; i < siblings.length; i++) {
    var sibling = $(siblings[i]);
    if (sibling[0].nodeType === 1) {
      var offset = sibling.offset().left;
      if (offset - w < x && offset + w > x) {
        return sibling;
      }
    }
  }
  return null;
}
ul {
  display: flex;
  flex-wrap: wrap;
  list-style: none;
}
li {
  width: 50px;
  height: 50px;
  margin: 5px;
  border: 1px solid red;
}
li.selected {
  background: red;
}
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
  <li class="selected"></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
</ul>

A couple ideas:

1) You could always just calculate the number that are shown on each line since you can get the width of the page.

2) You could use document.elementFromPoint and test a point that is below your current element.

Get the offsetLeft property of the current element.

var currentLeft = current.prop("offsetLeft");

Look at each next or previous element until you find the one with the same offsetLeft property.

case 38:
  var el = current.prev();
  while (el.length && el.prop("offsetLeft") != currentLeft) {
    el = el.prev();
  }
  if (el.length) {
    current.removeClass("selected");
    el.addClass("selected");
  }
  break;

$(document).on('keydown', function(e) {
  console.log(e.which);

  var current = $('li.selected');
  var currentLeft = current.prop("offsetLeft");

  switch (e.which) {
case 37:
  current.prev().addClass('selected').siblings().removeClass('selected');
  break;

case 39:
  current.next().addClass('selected').siblings().removeClass('selected');
  break;

case 38:
  var el = current.prev();
  while (el.length && el.prop("offsetLeft") != currentLeft) {
    el = el.prev();
  }
  if (el.length) {
    current.removeClass("selected");
    el.addClass("selected");
  }
  break;

case 40:
  var el = current.next();
  while (el.length && el.prop("offsetLeft") != currentLeft) {
    el = el.next();
  }
  if (el.length) {
    current.removeClass("selected");
    el.addClass("selected");
  }
  break;

default:
  return true;
  }

  e.preventDefault();
});

$('li').on('click', function(e) {
  $(this).addClass('selected').siblings().removeClass('selected');
});
ul {
  display: flex;
  flex-wrap: wrap;
  list-style: none;
}
li {
  width: 50px;
  height: 50px;
  margin: 5px;
  border: 1px solid red;
}
li.selected {
  background: red;
}
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
  <li class="selected"></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
</ul>

发布评论

评论列表(0)

  1. 暂无评论