当父级可调整大小时,如何防止嵌套 DIV 的内容溢出父级?

Posted

技术标签:

【中文标题】当父级可调整大小时,如何防止嵌套 DIV 的内容溢出父级?【英文标题】:How to prevent contents of nested DIV from overflowing parent when parent is resizable? 【发布时间】:2019-07-14 20:30:07 【问题描述】:

核心问题:我需要正确确定嵌套在 resizable 父级 <div> 中的子级 <div> 的高度。调整父级的大小时,子级不应超出父级的边界。应限制父级本身,以免其缩小超出子级的最小可滚动范围。

死胡同:通过数小时的研究,我了解到 CSS,即使使用 calc() 函数,也无法动态设置 <div> 的高度和宽度。此外,似乎为了防止子 <div> 溢出其父尺寸,父本身必须具有固定大小。在后一种情况下,像 height: auto; height: inherit;height: 100%; 这样的样式可能都产生相同的结果。

以前曾在这里提出过类似的问题,但我发现没有一个可以解决调整大小的问题。例如,为以下问题提供的解决方案无法正确解决我的问题。

Prevent child div from overflowing parent div

在我的情况下,我有一个<div>,它将被用户调整大小和/或拖动到屏幕的任何部分。如有必要,<div> 的内容应自动滚动。在任何情况下,它们都不应超过容器<div> 的尺寸,即可调整大小的容器。我的情况要求我不使用 jQuery,但欢迎使用 javascript 解决方案。 (即使是 PERL 也可能值得,因为我将提供页面并通过 Perl 平台上的 AJAX 对其进行更新。)

下面包含一个完整的示例,它显示了子 <div> 如何超过其父,即使启用了滚动,并且无论父 <div> 如何调整大小,都会发生这种情况。 (我已经在 Firefox 和 Safari 中测试过。)

请注意,在我的实际应用程序中,<div> 的内容将通过 AJAX 提供给每个用户从<select> 菜单中选择,并且内容的长度会有所不同。无论返回的长度如何,容器/父级<div> 应根据用户自己的调整保持相同的大小。

<!DOCTYPE html>
<HTML lang="utf8">
<head>
<title>Example of Resizable/Draggable Container Div with Nested (Overflowing) Contents</title>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">

<style type="text/css">
.nested 
  background-color: #ffffcc;
  overflow-y: auto;
  overflow-x: hidden;
  max-height: 720px;
  height: inherit;
  color: #000;
  text-align: left;
  font-weight: normal;
   
.resizable 
  background: white;
  min-width: 100px;
  min-height: 120px;
  width: 100%;
  height: 100%;
  position: absolute;
  top: 80px;
  left: 60px;
display: -webkit-box;   /* OLD - ios 6-, Safari 3.1-6, BB7 */
display: -ms-flexbox;  /* TWEENER - IE 10 */
display: -webkit-flex; /* NEW - Safari 6.1+. iOS 7.1+, BB10 */
display: flex;         /* NEW, Spec - Firefox, Chrome, Opera */
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;

.resizable .resizers
  width: 100%;
  height: 100%;
  border: 3px solid #4286f4;
  box-sizing: border-box;

.resizable .resizers .resizer
  width: 3px;
  height: 5px;
  border-radius: 50%;  /*magic to turn square into circle*/
  background: black;
  border: 3px solid #4286f4;
  position: absolute;
  
.resizable .resizers .resizer.top-left 
  left: -2px;
  top: -2px;
  cursor: nwse-resize; /*resizer cursor*/

.resizable .resizers .resizer.top-right 
  right: -2px;
  top: -2px;
  cursor: nesw-resize;

.resizable .resizers .resizer.bottom-left 
  left: -2px;
  bottom: -2px;
  cursor: nesw-resize;

.resizable .resizers .resizer.bottom-right 
  right: -2px;
  bottom: -2px;
  cursor: nwse-resize;
  
#dragselection 
    position: absolute;
    top: 6px;
    left: 3%;
    z-index: 8;
    background-color: #f1f1f1;
    border: 1px solid #d3d3d3;
display: -webkit-box;   /* OLD - iOS 6-, Safari 3.1-6, BB7 */
display: -ms-flexbox;  /* TWEENER - IE 10 */
display: -webkit-flex; /* NEW - Safari 6.1+. iOS 7.1+, BB10 */
display: flex;         /* NEW, Spec - Firefox, Chrome, Opera */
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;

.header 
    padding: 4px;
    margin-left: 2px;
    margin-right: 2px;
    margin-top: 4px;
    margin-bottom: 4px;
    text-align: center;
    cursor: move;
    z-index: 10;
    background-color: #2168a8;
    color: #fff;
    font-size: 14px;
    font-weight: bold;
    
#CV1 
    min-width:150px; 
    min-height:150px; 
        
.top-banner 
display: inline-block; 
margin-top: 0px; 
margin-right: 7px; 
margin-left: 5px; 
margin-bottom: 7px; 
background-color: #afb; 
border-radius: 12px; 
opacity: 0.9; 
box-shadow: 3px 3px #878087; 
max-width: 100%; 
height: auto;
align: auto;
text-align: center;
         
</style>


</head>
<body onload="dragElement('dragselection');">

<div class="top-banner">
<p style="margin-top: 30px; margin-right: 60px; margin-left: 60px; margin-bottom: 30px; text-align:center; font-size: 2.0em; color:#fff;text-shadow: 2px 1px 2px #050050;">&#9840; Easy Text Reference &#9840;</p>
</div>

<form id="myform" name="nmyform" method="POST" accept-charset="utf-8" action="URL_for_CGI" >
<article>

<h1>Example:</h1>

<div id="flex-box" name="nflex-box" class="flex-container">

<div class="container" id="mainscreen" ondrop="drop(event)" ondragover="allowDrop(event)">
<div class="header flex-container-head" id="header-box" name="nheader-box">  

       
<div class='resizable' id="dragselection" style="border: 1px solid gray; font-size:18px; height: 250px; ">
  <div class='resizers'>
    <div class='resizer top-left'></div>
    <div class='resizer top-right'></div>
    <div class='resizer bottom-left'></div>
    <div class='resizer bottom-right'></div>
    
    <div id="dragselectionheader" class='header'>
    Your Text Selection
 
  <select id="get_text" name="nget_text"  onchange="getAjaxResult(['selection_text','recordnum'], 'POST'); ">
<option value="">---- Selection Menu ----</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</select>
   
   </div>
    
  <div id="CV1" class="nested">
  A sample text:
  
  In the annals of human history, the growth of nations, the rise and fall of empires, appear as if dependent on the will and prowess of man; the shaping of events seems, to a great degree, to be determined by his power, ambition, or caprice. But in the word of God the curtain is drawn aside, and we behold, above, behind, and through all the play and counterplay of human interest and power and passions, the agencies of the All-merciful One, silently, patiently working out the counsels of His own will.  (Prophets and Kings, p. 499)
  </div>
  </div>
</div>


</div>
</div>

</div>

</article>

</form>


<script type="text/javascript">

/* Make the DIV element draggable: */
dragElement(document.getElementById(("dragselection")));

function dragElement(elmnt) 
  var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  if (document.getElementById(elmnt.id + "header")) 
    /* if present, the header is where you move the DIV from: */
    document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
   else 
    /* otherwise, move the DIV from anywhere inside the DIV: */
    elmnt.onmousedown = dragMouseDown;
  

  function dragMouseDown(e) 
    e = e || window.event;
    /* get the mouse cursor position at startup: */
    pos3 = e.clientX;
    pos4 = e.clientY;
    document.onmouseup = closeDragElement;
    /* call a function whenever the cursor moves: */
    document.onmousemove = elementDrag;
  

  function elementDrag(e) 
    e = e || window.event;
    /* calculate the new cursor position: */
    pos1 = pos3 - e.clientX;
    pos2 = pos4 - e.clientY;
    pos3 = e.clientX;
    pos4 = e.clientY;
    /* set the element's new position: */
    if ((elmnt.offsetTop - pos2) < 0)  
         elmnt.style.top = '0px'
     else      
         elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
         
    if ((elmnt.offsetLeft - pos1) < 0) 
         elmnt.style.left = '0px'
     else 
         elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
    
  

  function closeDragElement() 
    /* stop moving when mouse button is released:*/
    document.onmouseup = null;
    document.onmousemove = null;    
  


/* Modeled after: 
https://medium.com/the-z/making-a-resizable-div-in-js-is-not-easy-as-you-think-bda19a1bc53d 
*/

function makeResizableDiv(div) 
  const element = document.querySelector(div);
  const resizers = document.querySelectorAll(div + ' .resizer')
  const minimum_size = 120;
  var original_width = 0;
  var original_height = 0;
  var original_x = 0;
  var original_y = 0;
  var original_mouse_x = 0;
  var original_mouse_y = 0;

  for (var i = 0;i < resizers.length; i++) 
    const currentResizer = resizers[i];
    currentResizer.addEventListener('mousedown', function(e) 
      e.preventDefault()
      original_width = parseFloat(getComputedStyle(element, null).getPropertyValue('width').replace('px', ''));
      original_height = parseFloat(getComputedStyle(element, null).getPropertyValue('height').replace('px', ''));
      original_x = element.getBoundingClientRect().left;
      original_y = element.getBoundingClientRect().top;
      original_mouse_x = e.pageX;
      original_mouse_y = e.pageY;
      window.addEventListener('mousemove', resize)
      window.addEventListener('mouseup', stopResize)
    )
    
    function resize(e) 
      if (currentResizer.classList.contains('bottom-right')) 
        const width = original_width + (e.pageX - original_mouse_x);     
        const height = original_height + (e.pageY - original_mouse_y)     
        if (width > minimum_size) 
          element.style.width = width + 'px'     
          element.style.left = original_x + 'px' 
        
        if (height > minimum_size) 
          element.style.height = height + 'px'     
          element.style.top = original_y + 'px' 
        
      
      else if (currentResizer.classList.contains('bottom-left')) 
        const height = original_height + (e.pageY - original_mouse_y)     
        const width = original_width - (e.pageX - original_mouse_x)     
        if (height > minimum_size) 
          element.style.height = height + 'px'     
          element.style.top = original_y+'px' 
        
        if (width > minimum_size) 
          element.style.width = width + 'px'     
          element.style.left = original_x+(e.pageX-original_mouse_x)+'px' 
        
      
      else if (currentResizer.classList.contains('top-right')) 
        const width = original_width + (e.pageX - original_mouse_x)     
        const height = original_height - (e.pageY - original_mouse_y)     
        if (width > minimum_size) 
          element.style.width = width + 'px'     
          element.style.left = original_x + 'px'
        
        if (height > minimum_size) 
          if (original_y+(e.pageY-original_mouse_y) < 0 ) 
            element.style.top = '0px'
           else 
            element.style.height = height + 'px'     
            element.style.top = original_y+(e.pageY-original_mouse_y)+'px' 
          
        
      
      else           /* top-left */
        const width = original_width - (e.pageX - original_mouse_x)     
        const height = original_height - (e.pageY - original_mouse_y)     
        if (width > minimum_size) 
          element.style.width = width + 'px'     
          element.style.left = original_x+(e.pageX-original_mouse_x)+'px' 
        
        if (height > minimum_size) 
         if (original_y+(e.pageY-original_mouse_y) < 0 ) 
            element.style.top = '0px'
           else 
            element.style.height = height + 'px'     
            element.style.top = original_y+(e.pageY-original_mouse_y)+'px' 
          
        
      
    
    
    function stopResize() 
      window.removeEventListener('mousemove', resize)
    
  

makeResizableDiv('.resizable')

</script>
</body>
</html>

更新:我已经调整了上面的代码以反映目前提供的建议。不幸的是,我无法使用这种配置来限制父级的宽度。任何低于 100%(或自动)的东西都会被孩子继承,结果很不利。但是,我不希望父级占据屏幕的整个宽度,所以这个解决方案是不够的。

我现在想出了一个可以与 Firefox 一起使用的解决方法。我的 Safari 似乎不接受 flex 选项,所以仍然没有跨浏览器的解决方案。对于 Firefox,以下 JavaScript 代码可以在页面加载后设置&lt;div&gt; 位置,如下:

document.getElementById('dragselection').setAttribute("style","width:320px; height:240px; font-size:18px;");

我现在了解到以下代码更能跨浏览器兼容,并且可以让 Safari 正确呈现它。

display: -webkit-box;   /* OLD - iOS 6-, Safari 3.1-6, BB7 */
display: -ms-flexbox;  /* TWEENER - IE 10 */
display: -webkit-flex; /* NEW - Safari 6.1+. iOS 7.1+, BB10 */
display: flex;         /* NEW, Spec - Firefox, Chrome, Opera */
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;

我已在示例脚本的两个位置更新了该代码。这在此时可能是可行的。

【问题讨论】:

【参考方案1】:

你可以在这里使用 flexbox css。父 DIV 将是 flex, flex-direction: column.

另外对于最小可拖动屏幕,父级高度为120px,而子级最小高度为150px,从#CV1中删除样式。

你能更新这些样式并检查一下吗?

.resizable .resizers
  width: 100%;
  height: 100%;
  border: 3px solid #4286f4;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;


.nested 
  background-color: #ffffcc;
  overflow-y: auto;
  overflow-x: hidden;
  max-height: 720px;
  height: inherit;
  color: #000;
  text-align: left;
  font-weight: normal;
  

【讨论】:

感谢您的帮助。您的代码部分解决了 Firefox 的问题,但没有解决我的 Safari 的问题。当我根据您的建议更新样式时,父 div 会扩展到屏幕的宽度(两个浏览器),但里面的内容仍然在 Safari 的底部溢出,如覆盖蓝线并在下方延伸的背景色所示它。 从 #CV1 中移除样式,并从 .nested 类中移除 max-height。更新了上面的样式。希望这行得通! 我已经以多种方式使用了变量,但仍然无法让事情正常工作。 Safari 仍然显示子 div 溢出其父级,如果宽度设置为 100% 以外的任何值,Firefox 将无法正确显示。一定有更好的解决方案。

以上是关于当父级可调整大小时,如何防止嵌套 DIV 的内容溢出父级?的主要内容,如果未能解决你的问题,请参考以下文章

外部 jquery 可拖动/可调整大小的 div 不允许我选择内部跨度的文本

Jquery 可调整大小的问题,其中 div 合同的宽度和高度为零

Jquery UI 可排序和可调整大小的 Div

如何创建多个受容器 div 约束的可调整大小的同级 div 元素

jQuery UI 可调整大小:单独使用东手柄时的自动高度

在可拖动和可调整大小的 Jquery div 中删除