Как предотвратить отсечение абсолютно позиционированного всплывающего окна из его контейнера?

#javascript #html #css

#javascript #HTML #css

Вопрос:

 function myclick() {
  let node = document.createElement("div");
  node.className = "popover";
  node.innerText = "hello world hello world";
  event.srcElement.appendChild(node);
}  
 .container {
  width: 230px;
  height: 200px;
  border: 1px solid #ddd;
  display: flex;
  flex-flow: wrap;
}

.tag {
  border: 1px solid #ddd;
  padding: 5px;
  margin: 5px;
  display: flex;
  height: 20px;
  position: relative;
}

.popover {
  position: absolute;
  top: 30px;
  width: 60px;
  left: 0px;
  background-color: #dfdfdf;
}  
 <div class="container">
  <div class="tag" onclick="myclick();">apple</div>
  <div class="tag" onclick="myclick();">banana</div>
  <div class="tag" onclick="myclick();">carrot</div>
  <div class="tag" onclick="myclick();">dog</div>
  <div class="tag" onclick="myclick();">elephant</div>
  <div class="tag" onclick="myclick();">firetruck</div>
</div>  

Я разработал полное воспроизведение проблемы, с которой я столкнулся выше. В принципе, у меня всплывающие окна появляются из тегов, когда вы нажимаете на них. Все это работает отлично, за исключением случаев, когда тег находится рядом с краем его контейнера. В этом случае тег вырезается из контейнера. В приведенном примере, если вы нажмете «dog», всплывающее окно будет вырезано за пределами container div.

Скорее, я хотел бы, чтобы всплывающее окно перемещалось влево настолько, насколько это необходимо, чтобы предотвратить само отсечение контейнера. Я некоторое время возился с этим, но я не могу понять, как это сделать.

Комментарии:

1. Я бы создал класс css для popper (что-то вроде .popper.right ), который устанавливает left: auto; right: 0 , а затем добавил этот класс в вашу myClick функцию для элементов на краю. Но это сложная часть, вам нужно будет динамически определять, что находится на краю, на основе ширины контейнера и ширины каждого дочернего элемента.

2. Невозможно сделать это только с помощью CSS, но вам понадобится JS: вы могли бы получить ширину всех .tag элементов и (для каждого элемента) вычислить сумму ширин всех предыдущих элементов плюс их поля, и если текущий .tag элемент имеет ширину менее 60 пикселей (т. Е. Ширину ваших выпадающих списков), вы могли бы выровнять этот выпадающий список по .tag элементу. Но начиная со второй строки, это становится более сложным, поскольку вам придется перезапустить вычисление общей ширины элементов в каждой строке…

Ответ №1:

Вы можете попробовать это. Я добавил вызываемую функцию javascript onEdge , которая, предположительно, определяет, насколько близко дочерний элемент находится к краю. Это, вероятно, потребуется изменить для ваших нужд (например, установить, насколько близко к родительскому краю вы считаете «слишком близко»).

 function onEdge(child) {
  if (child.parentElement.offsetWidth < (child.offsetLeft   child.offsetWidth))
    return true;
  return false;
}

function myclick(elm) {
  var addClass = onEdge(elm);

  let node = document.createElement("div");
  node.className = "popover"   (addClass ? " right" : "");
  node.innerText = "hello world hello world";
  event.srcElement.appendChild(node);
}  
 .container {
  width: 230px;
  height: 200px;
  border: 1px solid #ddd;
  display: flex;
  flex-flow: wrap;
}

.tag {
  border: 1px solid #ddd;
  padding: 5px;
  margin: 5px;
  display: flex;
  height: 20px;
  position: relative;
}

.popover {
  position: absolute;
  top: 30px;
  width: 60px;
  left: 0px;
  background-color: #dfdfdf;
}

.popover.right {
  left: auto;
  right: 0;
}  
 <div id="tag-container" class="container">
  <div class="tag" onclick="myclick(this);">apple</div>
  <div class="tag" onclick="myclick(this);">banana</div>
  <div class="tag" onclick="myclick(this);">carrot</div>
  <div class="tag" onclick="myclick(this);">dog</div>
  <div class="tag" onclick="myclick(this);">elephant</div>
  <div class="tag" onclick="myclick(this);">firetruck</div>
</div>  

Комментарии:

1. О, это очень приятно, мне особенно нравится popover.right трюк. Пробуем сейчас.

2. К сожалению, у меня возникают трудности с получением, offsetLeft поскольку элементы в моем коде должны быть position: relative , поэтому он просто выдает мне 7 все значения offsetLefts.. Хм..

3. хм, интересно. В приведенном выше фрагменте это, кажется, работает. Должен ли ваш контейнер также быть position: relaitive ? tag Уже есть

4. Я знаю, что есть проблемы с левым и правым полями, потому что это не будет включено в offsetWidth значение дочерних элементов, но повлияет на его offsetLeft значение. Но если ваши поля статичны, вы должны быть в состоянии понять это (возможно, придется добавить значения полей к дочерней ширине в функции onEdge для большей точности)

Ответ №2:

ОТРЕДАКТИРОВАНО: Вы можете вычислить правильную точку тега и проверить, достаточно ли места во всплывающем окне. Добавьте параметр в myclick и передайте элемент.

 var popUpWidth = 60;
var containerWidth = 200;
function myclick(element) {
  let node = document.createElement("div");

  if(element.offsetLeft   popUpWidth > containerWidth){
     node.style.left = -20   "px";
  }

  node.className = "popover";
  node.innerText = "hello world hello world";
  event.srcElement.appendChild(node);
}
  

Снипет:

 var popUpWidth = 60;
var containerWidth = 200;
function myclick(element) {
  let node = document.createElement("div");

  if(element.offsetLeft   popUpWidth > containerWidth){
     node.style.left = -20   "px";
  }

  node.className = "popover";
  node.innerText = "hello world hello world";
  event.srcElement.appendChild(node);
}  
 .container {
  width: 230px;
  height: 200px;
  border: 1px solid #ddd;
  display: flex;
  flex-flow: wrap;
}

.tag {
  border: 1px solid #ddd;
  padding: 5px;
  margin: 5px;
  display: flex;
  height: 20px;
  position: relative;
}

.popover {
  position: absolute;
  top: 30px;
  width: 60px;
  left: 0px;
  background-color: #dfdfdf;
}  
 <div class="container">
  <div class="tag" onclick="myclick(this);">apple</div>
  <div class="tag" onclick="myclick(this);">banana</div>
  <div class="tag" onclick="myclick(this);">carrot</div>
  <div class="tag" onclick="myclick(this);">dog</div>
  <div class="tag" onclick="myclick(this);">elephant</div>
  <div class="tag" onclick="myclick(this);">firetruck</div>
</div>  

Комментарии:

1. К сожалению, это также приводит к перемещению других всплывающих окон влево, что нежелательно. Только когда всплывающее окно вырезается из своего контейнера, оно должно перемещаться влево (и только настолько, насколько это необходимо для предотвращения отсечения)

2. да, это похоже на установку right: 0; вместо left: 0;

3. Итак, вы хотите myclick быть общей функцией для каждого тега, а не только dog?

4. Вы можете рассчитать, достаточно ли места для всплывающего окна. Я внес некоторые изменения.

5. К сожалению, что-то подобное нужно сделать. Ну что ж, если другого ответа не придет, я приму этот, спасибо.

Ответ №3:

Просто слегка уменьшите левую позицию:

 .popover {
  position: absolute;
  top: 30px;
  width: 60px;
  left: -19px;
  background-color: #dfdfdf;
}
  

Комментарии:

1. Верно, я полагаю, что это сработало бы в этом случае, но я ищу более общее решение. Всплывающие окна в моем приложении намного шире и должны поддерживать свой размер.

2. Извините, это действительно должно быть динамическим, а не жестко запрограммированным.