Làm thế nào tôi có thể chuyển đổi chiều cao: 0; đến chiều cao: tự động; sử dụng CSS?


2135

Tôi đang cố gắng để làm cho một <ul> slide bằng cách sử dụng các chuyển tiếp CSS.

Việc <ul>bắt đầu lúc height: 0;. Khi di chuột, chiều cao được đặt thành height:auto;. Tuy nhiên, điều này khiến nó chỉ đơn giản xuất hiện, không chuyển tiếp,

Nếu tôi làm nó từ height: 40px;đến height: auto;, thì nó sẽ trượt lênheight: 0; , và sau đó đột nhiên nhảy lên độ cao chính xác.

Làm thế nào khác tôi có thể làm điều này mà không cần sử dụng JavaScript?

#child0 {
  height: 0;
  overflow: hidden;
  background-color: #dedede;
  -moz-transition: height 1s ease;
  -webkit-transition: height 1s ease;
  -o-transition: height 1s ease;
  transition: height 1s ease;
}
#parent0:hover #child0 {
  height: auto;
}
#child40 {
  height: 40px;
  overflow: hidden;
  background-color: #dedede;
  -moz-transition: height 1s ease;
  -webkit-transition: height 1s ease;
  -o-transition: height 1s ease;
  transition: height 1s ease;
}
#parent40:hover #child40 {
  height: auto;
}
h1 {
  font-weight: bold;
}
The only difference between the two snippets of CSS is one has height: 0, the other height: 40.
<hr>
<div id="parent0">
  <h1>Hover me (height: 0)</h1>
  <div id="child0">Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>
  </div>
</div>
<hr>
<div id="parent40">
  <h1>Hover me (height: 40)</h1>
  <div id="child40">Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>Some content
    <br>
  </div>
</div>


Tôi tin rằng height:auto/max-heightgiải pháp sẽ chỉ hoạt động nếu diện tích mở rộng của heightbạn lớn hơn mức bạn muốn hạn chế. Nếu bạn có một max-heightsố 300px, nhưng một thả xuống combo box, có thể quay trở lại 50px, sau đó max-heightsẽ không giúp bạn, 50pxcó thể thay đổi tùy thuộc vào số lượng các yếu tố, bạn có thể đến với một tình huống không thể nơi tôi không thể sửa chữa nó vì heightkhông phải là đã sửa, height:autolà giải pháp, nhưng tôi không thể sử dụng hiệu ứng chuyển tiếp với cái này.
Christopher Thomas

51
OP đang cố gắng cho giải pháp css, không phải js, nếu không họ chỉ có thể sử dụng tràn và hoạt hình
Toni Leigh

5
@VIDesignz: Nhưng đỉnh -100% của div bên trong nhận được chiều rộng của div trình bao bọc, không phải chiều cao. Vì vậy, giải pháp này có cùng một loại vấn đề, đó là giải pháp chiều cao tối đa. Ngoài ra, khi chiều rộng nhỏ hơn chiều cao của nội dung, không phải tất cả nội dung đều bị ẩn -100% trên cùng. Vì vậy, đây là một giải pháp sai.
GregTom

1
Xem github.com/w3c/csswg-dcraft/issues/626 để thảo luận về thông số kỹ thuật và triển khai giải pháp phù hợp
Ben Creasy

Câu trả lời:


2745

Sử dụng max-heighttrong quá trình chuyển đổi và không height. Và đặt một giá trị trênmax-height cho một cái gì đó lớn hơn hộp của bạn sẽ nhận được.

Xem bản demo của JSFiddle do Chris Jordan cung cấp trong một câu trả lời khác tại đây.

#menu #list {
    max-height: 0;
    transition: max-height 0.15s ease-out;
    overflow: hidden;
    background: #d5d5d5;
}

#menu:hover #list {
    max-height: 500px;
    transition: max-height 0.25s ease-in;
}
<div id="menu">
    <a>hover me</a>
    <ul id="list">
        <!-- Create a bunch, or not a bunch, of li's to see the timing. -->
        <li>item</li>
        <li>item</li>
        <li>item</li>
        <li>item</li>
        <li>item</li>
    </ul>
</div>


324
công việc này thật tuyệt ngoại trừ có độ trễ khi nó bắt đầu, bởi vì nó bắt đầu cho chiều cao tối đa ban đầu rất cao..hmm, tôi nghĩ điều này hơi khó chịu
vsync

175
+1 Giải pháp tuyệt vời! Tốc độ chuyển đổi được tính được tính bằng thời gian bạn chỉ định chuyển sang giá trị chiều cao tối đa ... nhưng vì chiều cao sẽ nhỏ hơn chiều cao tối đa, nên việc chuyển đổi sang chiều cao thực tế sẽ diễn ra nhanh hơn (thường là đáng kể) so với thời gian quy định.
kingjeffrey

50
Lưu ý rằng điều này có thể gây ra kết thúc chuyển tiếp xấu khi bạn phải sử dụng các giá trị lớn hơn nhiều so với giá trị tính toán thực tế. Tôi nhận thấy điều này trong khi cố gắng làm cho div tăng từ 0 chiều cao lên chiều cao nội dung thay đổi lớn do kích thước màn hình khác nhau (2 dòng trên màn hình 2560x1440 của tôi so với> 10 dòng trên điện thoại thông minh). Đối với điều này, tôi đã kết thúc với js.
jpeltoniemi

44
Giải pháp rất xấu vì nó tạo ra độ trễ theo một hướng nhưng không phải theo hướng khác.
Tim

84
Đây là một giải pháp khá lười biếng. Tôi giả sử OP muốn sử dụng chiều cao: tự động vì chiều cao mở rộng của container có phần khó đoán. Giải pháp này sẽ gây ra sự chậm trễ trước khi hình ảnh động hiển thị. Ngoài ra, thời lượng có thể nhìn thấy của hình ảnh động sẽ không thể đoán trước. Bạn sẽ nhận được nhiều kết quả dễ đoán hơn (và có thể mượt mà hơn) bằng cách tính chiều cao kết hợp của từng nút con của container và sau đó giảm xuống giá trị chiều cao chính xác.
Shawn Whinnery

314

Bạn nên sử dụng scaleY thay thế.

ul {
  background-color: #eee;
  transform: scaleY(0);    
  transform-origin: top;
  transition: transform 0.26s ease;
}
p:hover ~ ul {
  transform: scaleY(1);
}
<p>Hover This</p>
<ul>
  <li>Coffee</li>
  <li>Tea</li>
  <li>Milk</li>
</ul>

Tôi đã tạo một phiên bản tiền tố của nhà cung cấp trên jsfiddle và thay đổi jsfiddle của bạn để sử dụng scaleY thay vì chiều cao.


6
Tôi ủng hộ câu trả lời này vì nó hoạt động tốt trong các trình duyệt có khả năng chuyển đổi css3, nhưng cần lưu ý rằng điều này sẽ không hoạt động trong IE <9sẽ không hoạt động (nó sẽ chỉ nhảy) trong IE <10
Hailwood

215
Phương pháp này chỉ đạt được một phần hiệu quả mong muốn nhưng không thực sự loại bỏ không gian. Hộp biến đổi hoạt động giống như một yếu tố được định vị tương đối - không gian được chiếm bất kể nó được thu nhỏ như thế nào. Kiểm tra jsFiddle này lấy cái đầu tiên của bạn và chỉ cần thêm một số văn bản không có thật ở phía dưới. Lưu ý cách văn bản bên dưới nó không di chuyển lên khi chiều cao hộp được thu nhỏ về không.
phim hoạt hình

9
Bây giờ nó có: jsfiddle.net/gNDX3/1 Về cơ bản, bạn cần định kiểu các yếu tố của mình theo những gì bạn cần. Không có viên đạn bạc hoặc widget như hành vi trong CSS / HTML.
dotnetCarpenter

70
Trong khi tôi hoan nghênh ai đó đang thử các cách tiếp cận khác nhau, thì hiệu ứng và các biến chứng trong thế giới thực mà giải pháp này mang lại còn tồi tệ hơn nhiều so với hack chiều cao tối đa khủng khiếp. Xin đừng sử dụng.
mystrdat

2
@SapesCat tất cả đều đúng. Đây là một trong đó là ngăn kéo hơn như: jsfiddle.net/dotnetCarpenter/WTL2r
dotnetCarpenter

202

Hiện tại bạn không thể làm động với chiều cao khi một trong những độ cao có liên quan là auto, bạn phải đặt hai độ cao rõ ràng.


12
Thường có nhiều cách để giải quyết vấn đề và không phải tất cả chúng đều phù hợp hoặc có thể. Tôi không biết tại sao @JedidiahHurt và Ryan Ore đang cố gắng hạn chế các câu trả lời có thể có trên trang web Hỏi & Đáp. Đặc biệt là xem xét câu trả lời này là ở đây đầu tiên. Chắc chắn là như vậy vì câu trả lời được chấp nhận là một cách giải quyết.
Hoan hô tôi giúp

5
Câu trả lời từ @jake không thanh lịch cũng không chính xác. Câu trả lời liên kết ở đây là tốt hơn nhiều. Nó hoạt động chính xác và xử lý tất cả các trường hợp kích thước - không thể nói câu trả lời được chấp nhận.
GraphicsMuncher

5
Yup: đúng vậythis.$element[dimension](this.$element[dimension]())[0].offsetHeight
Andy

4
Đây có phải là kế hoạch để được sửa chữa bằng cách nào đó? Ý tôi là, điều này khá tự nhiên để mong có thể chuyển từ 0 độ cao sang auto...
Augustin Riedinger

6
@Meliodas "không / bạn không thể" là câu trả lời hợp lệ và có thể chấp nhận được cho các câu hỏi trên Stack Overflow.
TylerH

122

Các giải pháp mà tôi đã luôn luôn được sử dụng là để ra phai trước, sau đó thu nhỏ font-size, paddingmargingiá trị. Nó trông không giống như một cái lau, nhưng nó hoạt động mà không có tĩnh heighthoặcmax-height .

Ví dụ làm việc:

/* final display */
#menu #list {
    margin: .5em 1em;
    padding: 1em;
}

/* hide */
#menu:not(:hover) #list {
    font-size: 0;
    margin: 0;
    opacity: 0;
    padding: 0;
    /* fade out, then shrink */
    transition: opacity .25s,
                font-size .5s .25s,
                margin .5s .25s,
                padding .5s .25s;
}

/* reveal */
#menu:hover #list {
    /* unshrink, then fade in */
    transition: font-size .25s,
                margin .25s,
                padding .25s,
                opacity .5s .25s;
}
<div id="menu">
    <b>hover me</b>
    <ul id="list">
        <li>item</li>
        <li>item</li>
        <li>item</li>
        <li>item</li>
        <li>item</li>
    </ul>
</div>

<p>Another paragraph...</p>


Làm việc gọn gàng cho tôi. Không hoàn toàn giống như slideUpUp / Down. Bạn chỉ có thể thấy sự khác biệt với các CPU yếu, như máy tính cũ hoặc thiết bị di động, đồng thời mở và đóng nhiều trong số chúng.
Cruz Nunez

3
Nếu bạn có các nút hoặc bất kỳ thành phần phi văn bản nào khác, bạn sẽ kết thúc với khoảng trắng không mong muốn trong khi không di chuột.
Dennis W

3
Bạn có thể tinh chỉnh thêm bằng cách chọn tất cả các phần tử con *và áp dụng các thay đổi khác. Các nút nên bị ảnh hưởng bởi mã trên của tôi, tuy nhiên, nếu chúng được tạo kiểu chính xác em.
Steven Vachon

1
Trong trường hợp của tôi, tôi cũng cần phải thêm line-height: 0;border-width: 0;
Andrey Shatilov

1
Bạn không cần điều chỉnh line-heightnếu bạn tránh chính xác các đơn vị trên giá trị: developer.mozilla.org/en-US/docs/Web/CSS/iêu
Steven Vachon

93

Tôi biết đây là câu trả lời thứ ba mươi cho câu hỏi này, nhưng tôi nghĩ nó đáng giá, vì vậy hãy đi. Đây là một giải pháp chỉ dành cho CSS với các thuộc tính sau:

  • Không có sự chậm trễ khi bắt đầu và quá trình chuyển đổi không dừng lại sớm. Theo cả hai hướng (mở rộng và thu gọn), nếu bạn chỉ định thời lượng chuyển đổi là 300ms trong CSS của mình, thì quá trình chuyển đổi sẽ mất 300ms, theo chu kỳ.
  • Đó là chuyển đổi chiều cao thực tế (không giống như transform: scaleY(0) ), do đó, nó thực hiện đúng nếu có nội dung sau phần tử đóng mở.
  • Mặc dù (như trong các giải pháp khác) có các số ma thuật (như "chọn độ dài cao hơn hộp của bạn"), nhưng sẽ không gây tử vong nếu giả định của bạn kết thúc là sai. Quá trình chuyển đổi có thể trông không tuyệt vời trong trường hợp đó, nhưng trước và sau quá trình chuyển đổi, đây không phải là vấn đề: Ở height: autotrạng thái mở rộng ( ), toàn bộ nội dung luôn có chiều cao chính xác (không giống như nếu bạn chọn một max-heightbiến hóa ra quá thấp). Và trong trạng thái sụp đổ, chiều cao bằng không.

Bản giới thiệu

Đây là bản demo với ba yếu tố có thể thu gọn, tất cả các độ cao khác nhau, tất cả đều sử dụng cùng một CSS. Bạn có thể muốn nhấp vào "toàn trang" sau khi nhấp vào "chạy đoạn trích". Lưu ý rằng JavaScript chỉ bật tắt collapsedlớp CSS, không có phép đo liên quan. (Bạn có thể thực hiện bản demo chính xác này mà không cần bất kỳ JavaScript nào bằng cách sử dụng hộp kiểm hoặc :target). Cũng lưu ý rằng phần CSS chịu trách nhiệm cho quá trình chuyển đổi khá ngắn và HTML chỉ yêu cầu một yếu tố trình bao bọc bổ sung duy nhất.

$(function () {
  $(".toggler").click(function () {
    $(this).next().toggleClass("collapsed");
    $(this).toggleClass("toggled"); // this just rotates the expander arrow
  });
});
.collapsible-wrapper {
  display: flex;
  overflow: hidden;
}
.collapsible-wrapper:after {
  content: '';
  height: 50px;
  transition: height 0.3s linear, max-height 0s 0.3s linear;
  max-height: 0px;
}
.collapsible {
  transition: margin-bottom 0.3s cubic-bezier(0, 0, 0, 1);
  margin-bottom: 0;
  max-height: 1000000px;
}
.collapsible-wrapper.collapsed > .collapsible {
  margin-bottom: -2000px;
  transition: margin-bottom 0.3s cubic-bezier(1, 0, 1, 1),
              visibility 0s 0.3s, max-height 0s 0.3s;
  visibility: hidden;
  max-height: 0;
}
.collapsible-wrapper.collapsed:after
{
  height: 0;
  transition: height 0.3s linear;
  max-height: 50px;
}

/* END of the collapsible implementation; the stuff below
   is just styling for this demo */

#container {
  display: flex;
  align-items: flex-start;
  max-width: 1000px;
  margin: 0 auto;
}  


.menu {
  border: 1px solid #ccc;
  box-shadow: 0 1px 3px rgba(0,0,0,0.5);
  margin: 20px;

  
}

.menu-item {
  display: block;
  background: linear-gradient(to bottom, #fff 0%,#eee 100%);
  margin: 0;
  padding: 1em;
  line-height: 1.3;
}
.collapsible .menu-item {
  border-left: 2px solid #888;
  border-right: 2px solid #888;
  background: linear-gradient(to bottom, #eee 0%,#ddd 100%);
}
.menu-item.toggler {
  background: linear-gradient(to bottom, #aaa 0%,#888 100%);
  color: white;
  cursor: pointer;
}
.menu-item.toggler:before {
  content: '';
  display: block;
  border-left: 8px solid white;
  border-top: 8px solid transparent;
  border-bottom: 8px solid transparent;
  width: 0;
  height: 0;
  float: right;
  transition: transform 0.3s ease-out;
}
.menu-item.toggler.toggled:before {
  transform: rotate(90deg);
}

body { font-family: sans-serif; font-size: 14px; }

*, *:after {
  box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="container">
  <div class="menu">
    <div class="menu-item">Something involving a holodeck</div>
    <div class="menu-item">Send an away team</div>
    <div class="menu-item toggler">Advanced solutions</div>
    <div class="collapsible-wrapper collapsed">
      <div class="collapsible">
        <div class="menu-item">Separate saucer</div>
        <div class="menu-item">Send an away team that includes the captain (despite Riker's protest)</div>
        <div class="menu-item">Ask Worf</div>
        <div class="menu-item">Something involving Wesley, the 19th century, and a holodeck</div>
        <div class="menu-item">Ask Q for help</div>
      </div>
    </div>
    <div class="menu-item">Sweet-talk the alien aggressor</div>
    <div class="menu-item">Re-route power from auxiliary systems</div>
  </div>

  <div class="menu">
    <div class="menu-item">Something involving a holodeck</div>
    <div class="menu-item">Send an away team</div>
    <div class="menu-item toggler">Advanced solutions</div>
    <div class="collapsible-wrapper collapsed">
      <div class="collapsible">
        <div class="menu-item">Separate saucer</div>
        <div class="menu-item">Send an away team that includes the captain (despite Riker's protest)</div>
      </div>
    </div>
    <div class="menu-item">Sweet-talk the alien aggressor</div>
    <div class="menu-item">Re-route power from auxiliary systems</div>
  </div>

  <div class="menu">
    <div class="menu-item">Something involving a holodeck</div>
    <div class="menu-item">Send an away team</div>
    <div class="menu-item toggler">Advanced solutions</div>
    <div class="collapsible-wrapper collapsed">
      <div class="collapsible">
        <div class="menu-item">Separate saucer</div>
        <div class="menu-item">Send an away team that includes the captain (despite Riker's protest)</div>
        <div class="menu-item">Ask Worf</div>
        <div class="menu-item">Something involving Wesley, the 19th century, and a holodeck</div>
        <div class="menu-item">Ask Q for help</div>
        <div class="menu-item">Separate saucer</div>
        <div class="menu-item">Send an away team that includes the captain (despite Riker's protest)</div>
        <div class="menu-item">Ask Worf</div>
        <div class="menu-item">Something involving Wesley, the 19th century, and a holodeck</div>
        <div class="menu-item">Ask Q for help</div>
      </div>
    </div>
    <div class="menu-item">Sweet-talk the alien aggressor</div>
    <div class="menu-item">Re-route power from auxiliary systems</div>
  </div>

</div>

Làm thế nào nó hoạt động?

Thực tế có hai sự chuyển đổi liên quan đến việc thực hiện điều này. Một trong số chúng chuyển margin-bottomtừ 0px (ở trạng thái mở rộng) sang -2000pxtrạng thái thu gọn (tương tự câu trả lời này ). 2000 ở đây là con số ma thuật đầu tiên, nó dựa trên giả định rằng hộp của bạn sẽ không cao hơn số này (2000 pixel có vẻ như là một lựa chọn hợp lý).

Bản thân việc sử dụng margin-bottomquá trình chuyển đổi có hai vấn đề:

  • Nếu bạn thực sự có một hộp cao hơn 2000 pixel, thì margin-bottom: -2000pxsẽ không ẩn mọi thứ - sẽ có những thứ có thể nhìn thấy ngay cả trong trường hợp bị sập. Đây là một sửa chữa nhỏ mà chúng ta sẽ làm sau.
  • Nếu hộp thực tế cao 1000 pixel và quá trình chuyển đổi của bạn dài 300ms thì quá trình chuyển đổi có thể nhìn thấy đã kết thúc sau khoảng 150ms (hoặc, theo hướng ngược lại, bắt đầu trễ 150ms).

Khắc phục sự cố thứ hai này là nơi chuyển tiếp thứ hai đến và quá trình chuyển đổi này nhắm mục tiêu theo chiều cao tối thiểu của trình bao bọc ("về mặt khái niệm" vì chúng tôi không thực sự sử dụng thuộc min-heighttính cho việc này; nhiều hơn về sau).

Đây là một hình ảnh động cho thấy cách kết hợp chuyển đổi lề dưới với chuyển đổi chiều cao tối thiểu, cả hai thời lượng bằng nhau, cho chúng ta một chuyển đổi kết hợp từ chiều cao đầy đủ sang chiều cao bằng không có cùng thời lượng.

hoạt hình như mô tả ở trên

Thanh bên trái cho thấy cách lề dưới âm đẩy đáy lên trên, làm giảm chiều cao nhìn thấy được. Thanh giữa cho thấy chiều cao tối thiểu đảm bảo rằng trong trường hợp sụp đổ, quá trình chuyển đổi không kết thúc sớm và trong trường hợp mở rộng, quá trình chuyển đổi không bắt đầu muộn. Thanh bên phải cho thấy cách kết hợp cả hai làm cho hộp chuyển từ chiều cao đầy đủ sang chiều cao bằng không trong khoảng thời gian chính xác.

Đối với bản demo của tôi, tôi đã giải quyết trên 50px là giá trị chiều cao tối thiểu trên. Đây là con số ma thuật thứ hai và nó phải thấp hơn chiều cao của chiếc hộp. 50px có vẻ hợp lý là tốt; Có vẻ như bạn không thường xuyên muốn làm cho một yếu tố có thể thu gọn được thậm chí không cao 50 pixel ở vị trí đầu tiên.

Như bạn có thể thấy trong hình ảnh động, quá trình chuyển đổi kết quả là liên tục, nhưng nó không khác biệt - tại thời điểm khi chiều cao tối thiểu bằng với chiều cao đầy đủ được điều chỉnh bởi lề dưới, tốc độ thay đổi đột ngột. Điều này rất đáng chú ý trong hoạt hình vì nó sử dụng chức năng định thời tuyến tính cho cả hai lần chuyển đổi và vì toàn bộ quá trình chuyển đổi rất chậm. Trong trường hợp thực tế (bản demo của tôi ở trên cùng), quá trình chuyển đổi chỉ mất 300ms và chuyển đổi lề dưới không phải là tuyến tính. Tôi đã chơi xung quanh với rất nhiều chức năng thời gian khác nhau cho cả hai lần chuyển đổi và những chức năng tôi kết thúc với cảm giác như chúng hoạt động tốt nhất cho nhiều trường hợp khác nhau.

Vẫn còn hai vấn đề cần khắc phục:

  1. điểm từ trên cao, nơi các hộp có chiều cao hơn 2000 pixel không bị ẩn hoàn toàn trong trạng thái thu gọn,
  2. và vấn đề ngược lại, trong trường hợp không ẩn, các hộp có chiều cao dưới 50 pixel quá cao ngay cả khi quá trình chuyển đổi không chạy, vì chiều cao tối thiểu giữ chúng ở mức 50 pixel.

Chúng tôi giải quyết vấn đề đầu tiên bằng cách cung cấp cho phần tử container max-height: 0trong trường hợp bị sập, với một 0s 0.3schuyển đổi. Điều này có nghĩa là nó không thực sự là một quá trình chuyển đổi, nhưng nó max-heightđược áp dụng với độ trễ; nó chỉ áp dụng khi quá trình chuyển đổi kết thúc. Để điều này hoạt động chính xác, chúng ta cũng cần chọn một số max-heightcho trạng thái ngược lại, không sụp đổ. Nhưng không giống như trong trường hợp 2000px, khi việc chọn một số quá lớn ảnh hưởng đến chất lượng của quá trình chuyển đổi, trong trường hợp này, điều đó thực sự không quan trọng. Vì vậy, chúng tôi chỉ có thể chọn một số cao đến mức chúng tôi biết rằng sẽ không có chiều cao nào đến gần với điều này. Tôi đã chọn một triệu pixel. Nếu bạn cảm thấy có thể cần hỗ trợ nội dung có chiều cao hơn một triệu pixel, thì 1) Tôi xin lỗi và 2) chỉ cần thêm một vài số không.

Vấn đề thứ hai là lý do tại sao chúng ta không thực sự sử dụng min-heightcho việc chuyển đổi chiều cao tối thiểu. Thay vào đó, có một ::afterphần tử giả trong vùng chứa có phần heightchuyển từ 50px sang 0. Điều này có tác dụng tương tự như min-height: Nó sẽ không cho phép container co lại dưới bất kỳ chiều cao nào mà phần tử giả hiện có. Nhưng vì chúng tôi đang sử dụng heightchứ không phải min-heightbây giờ chúng tôi có thể sử dụng max-height(một lần nữa áp dụng độ trễ) để đặt chiều cao thực tế của phần tử giả thành 0 khi quá trình chuyển đổi kết thúc, đảm bảo rằng ít nhất là bên ngoài quá trình chuyển đổi, ngay cả các phần tử nhỏ cũng có chiều cao chính xác. Bởi vì min-heightmạnh hơn max-height, điều này sẽ không hoạt động nếu chúng tôi sử dụng các containermin-height thay vì của pseudo-yếu tốheight. Cũng giống như max-heighttrong đoạn trước, điều này max-heightcũng cần một giá trị cho phần cuối đối diện của quá trình chuyển đổi. Nhưng trong trường hợp này, chúng ta chỉ có thể chọn 50px.

Đã thử nghiệm trong Chrome (Win, Mac, Android, iOS), Firefox (Win, Mac, Android), Edge, IE11 (ngoại trừ vấn đề bố cục flexbox với bản demo của tôi mà tôi không bận tâm gỡ lỗi) và Safari (Mac, iOS ). Nói về flexbox, có thể làm cho công việc này hoạt động mà không cần sử dụng bất kỳ flexbox nào; thực tế tôi nghĩ rằng bạn có thể làm cho hầu hết mọi thứ hoạt động trong IE7 - ngoại trừ thực tế là bạn sẽ không có chuyển đổi CSS, làm cho nó trở thành một bài tập khá vô nghĩa.


35
Tôi ước bạn có thể rút ngắn (đơn giản hóa) giải pháp của mình hoặc ít nhất là ví dụ mã - có vẻ như 90% ví dụ mã không liên quan đến câu trả lời của bạn.
Gil Epshtain

Có cách nào để làm cho các chuyển đổi này cũng hoạt động nếu collapsible-wrappercó một vị trí tuyệt đối. Các overflow: hiddentrên cha mẹ là cần thiết cho quá trình chuyển đổi nhưng Cản với các yếu tố hoàn toàn vị trí.
pmkro

1
@pmkro Vâng, tôi cũng có vấn đề đó. Tôi đã giải quyết nó bằng cách sử dụng clip-path: polygon(...)thay overflow: hiddenvì bởi vì không giống như cái sau, bạn có thể chuyển đổi cái trước. Là một dự phòng cho các trình duyệt cũ hơn, tôi đã sử dụng một biến đổi tỷ lệ bổ sung. Xem nhận xét ở đầu github.com/StackExchange/Stacks/blob/develop/lib/css/components/
Kẻ

2
Đẹp. Điều này hoạt động thực sự tốt. Câu trả lời nên được chấp nhận
Henry Ing-Simmons

84

Bạn có thể, với một chút jiggery phi ngữ nghĩa-pokery. Cách tiếp cận thông thường của tôi là làm sống động chiều cao của một DIV bên ngoài có một con duy nhất là một DIV không có kiểu dáng chỉ được sử dụng để đo chiều cao nội dung.

function growDiv() {
  var growDiv = document.getElementById('grow');
  if (growDiv.clientHeight) {
    growDiv.style.height = 0;
  } else {
    var wrapper = document.querySelector('.measuringWrapper');
    growDiv.style.height = wrapper.clientHeight + "px";
  }
}
#grow {
  -moz-transition: height .5s;
  -ms-transition: height .5s;
  -o-transition: height .5s;
  -webkit-transition: height .5s;
  transition: height .5s;
  height: 0;
  overflow: hidden;
  outline: 1px solid red;
}
<input type="button" onclick="growDiv()" value="grow">
<div id='grow'>
  <div class='measuringWrapper'>
    <div>
      The contents of my div.
    </div>
    <div>
      The contents of my div.
    </div>
    <div>
      The contents of my div.
    </div>
    <div>
      The contents of my div.
    </div>
    <div>
      The contents of my div.
    </div>
    <div>
      The contents of my div.
    </div>
  </div>
</div>

Người ta chỉ muốn có thể phân phối với .measuringWrappervà chỉ đặt chiều cao của DIV thành tự động và có hoạt hình đó, nhưng điều đó dường như không hoạt động (chiều cao được đặt, nhưng không có hoạt hình nào xảy ra).

function growDiv() {
  var growDiv = document.getElementById('grow');
  if (growDiv.clientHeight) {
    growDiv.style.height = 0;
  } else {
    growDiv.style.height = 'auto';
  }
}
#grow {
  -moz-transition: height .5s;
  -ms-transition: height .5s;
  -o-transition: height .5s;
  -webkit-transition: height .5s;
  transition: height .5s;
  height: 0;
  overflow: hidden;
  outline: 1px solid red;
}
<input type="button" onclick="growDiv()" value="grow">
<div id='grow'>
  <div>
    The contents of my div.
  </div>
  <div>
    The contents of my div.
  </div>
  <div>
    The contents of my div.
  </div>
  <div>
    The contents of my div.
  </div>
  <div>
    The contents of my div.
  </div>
  <div>
    The contents of my div.
  </div>
</div>

Giải thích của tôi là một chiều cao rõ ràng là cần thiết để hoạt hình chạy. Bạn không thể có được một hình ảnh động về chiều cao khi chiều cao (chiều cao bắt đầu hoặc kết thúc) là auto.


10
Vì điều này phụ thuộc vào javascript, bạn cũng có thể dễ dàng thêm công cụ đo lường bằng cách sử dụng javascript!
Quickredfox

18
Bạn có thể làm điều đó mà không cần bọc. Chỉ: hàm growDiv () {var growDiv = document.getEuityById ('grow'); if (growDiv.clientHeight) {growDiv.style.height = 0; } other {growDiv.style.height = growDiv.scrollHeight + 'px'; }}
dùng1742529

8
Hãy xem điều này ... nói về một câu trả lời đơn giản jsfiddle.net/n5XfG/2596 ... Câu trả lời của bạn cực kỳ phức tạp mà không có lý do chính đáng. Không cần max-height, không cần autovà đặc biệt là không cần Javascript !! Chỉ cần hai tuyên bố đơn giản
VIDesignz

12
@VIDesignz Bạn animate trên lề: 100%. Đây là 100% chiều rộng của phần tử, không phải chiều cao của nó! Điều này có nghĩa, nếu bạn có một phần tử có chiều cao lớn hơn chiều rộng của nó, điều này sẽ không ẩn nó. Ngoài ra, tốc độ hoạt hình thực tế hoàn toàn khác so với tốc độ được xác định.
Timo Türschmann

3
Tôi rất hào hứng với câu trả lời của VIDesignz cho đến khi tôi đọc thêm về nhận xét của Timo. Có, margin-top(và margin-bottom) tương đối với chiều rộng khi được xác định theo tỷ lệ phần trăm, vâng, điều đó thật ngu ngốc, nhưng nó cũng giải thích tại sao thời gian hoạt hình mở và đóng hoàn toàn khác nhau - đó là điều tương tự bạn thấy trong câu trả lời được xếp hạng cao nhất, chỉ rõ độ khó mã hóa chiều cao tối đa. Tại sao điều này rất khó để làm đúng?
Bộ giải mã

52

Một cách giải quyết trực quan để tạo hiệu ứng chiều cao bằng cách sử dụng các chuyển tiếp CSS3 là để tạo hiệu ứng cho phần đệm thay thế.

Bạn hoàn toàn không có được hiệu ứng xóa hoàn toàn, nhưng chơi xung quanh với các giá trị thời gian chuyển tiếp và phần đệm sẽ giúp bạn đủ gần. Nếu bạn không muốn đặt rõ ràng chiều cao / chiều cao tối đa, đây sẽ là thứ bạn đang tìm kiếm.

div {
    height: 0;
    overflow: hidden;
    padding: 0 18px;
    -webkit-transition: all .5s ease;
       -moz-transition: all .5s ease;
            transition: all .5s ease;
}
div.animated {
    height: auto;
    padding: 24px 18px;
}

http://jsfiddle.net/catharsis/n5XfG/17/ (đã loại bỏ stephband trên jsFiddle)


3
Tôi nghĩ rằng đây là câu trả lời tốt nhất. Kỹ thuật này có thể mở rộng để điều chỉnh phần đệm trên trẻ em. Trong trường hợp của tôi, tôi đã có thể kết hợp nó với các chuyển đổi chiều cao rõ ràng trên một số nội dung được tiết lộ và hiệu ứng tổng thể thành công hơn nhiều so với công việc chiều cao tối đa, rất tốt cho việc tiết lộ nhưng giới thiệu một khoảnh khắc khó xử của trì hoãn ẩn. Tôi sẽ sớm không hoạt động hơn là giới thiệu một sự chậm trễ vô nghĩa khiến ứng dụng của tôi dường như không phản hồi. Tôi ngạc nhiên rất nhiều người dường như nghĩ rằng đó là chấp nhận được.
Dấu chấm phẩy

17
Ngoại trừ ở đây bạn không hoạt hình chiều cao nào cả. Bạn đang tạo hiệu ứng cho phần đệm ... nó có thể biến mất hoàn toàn vì nó có thể hoạt hình từ trạng thái hiện tại xuống 0, nhưng nếu bạn quan sát kỹ khi nó mở rộng, nó sẽ mở ra với văn bản và sau đó phần đệm chỉ hoạt hình .. bởi vì nó không hoạt động 'không biết làm thế nào để hoạt hình từ 0 đến tự động .... nó cần một phạm vi số ... đó là cách tweening hoạt động.
Rob R

Đây là một cách giải quyết tốt mà không hacky. Đây không phải là câu trả lời tốt nhất cho câu hỏi này , nhưng nó là một giải pháp thay thế phù hợp với tôi. Đôi khi bạn chỉ cần hiệu ứng của một số hoạt hình chiều cao, không phải hoạt hình đầy đủ :)
Ivan Durst

Giải pháp này cũng thất bại khi sử dụng độ trễ chuyển tiếp.
Michel Joanisse

Tôi cũng đồng ý rằng giải pháp này là một cách giải quyết rõ ràng, cung cấp các chuyển tiếp khá trôi chảy nếu hoạt hình của bạn đủ nhanh (trong trường hợp của tôi cho một hiệp ước có chuyển tiếp 0,1 giây là hoàn hảo!). Mặc dù vậy, nó sẽ không có rất nhiều ứng dụng, nhưng những người khác sẽ không trả lời câu hỏi này!
Remi D

46

Cách giải quyết của tôi là chuyển đổi chiều cao tối đa sang chiều cao nội dung chính xác để có hiệu ứng hoạt hình mượt mà, sau đó sử dụng hàm gọi lại chuyển đổi để đặt chiều cao tối đa thành 9999px để nội dung có thể thay đổi kích thước một cách tự do.

var content = $('#content');
content.inner = $('#content .inner'); // inner div needed to get size of content when closed

// css transition callback
content.on('transitionEnd webkitTransitionEnd transitionend oTransitionEnd msTransitionEnd', function(e){
    if(content.hasClass('open')){
        content.css('max-height', 9999); // try setting this to 'none'... I dare you!
    }
});

$('#toggle').on('click', function(e){
    content.toggleClass('open closed');
    content.contentHeight = content.outerHeight();
    
    if(content.hasClass('closed')){
        
        // disable transitions & set max-height to content height
        content.removeClass('transitions').css('max-height', content.contentHeight);
        setTimeout(function(){
            
            // enable & start transition
            content.addClass('transitions').css({
                'max-height': 0,
                'opacity': 0
            });
            
        }, 10); // 10ms timeout is the secret ingredient for disabling/enabling transitions
        // chrome only needs 1ms but FF needs ~10ms or it chokes on the first animation for some reason
        
    }else if(content.hasClass('open')){  
        
        content.contentHeight += content.inner.outerHeight(); // if closed, add inner height to content height
        content.css({
            'max-height': content.contentHeight,
            'opacity': 1
        });
        
    }
});
.transitions {
    transition: all 0.5s ease-in-out;
    -webkit-transition: all 0.5s ease-in-out;
    -moz-transition: all 0.5s ease-in-out;
}

body {
    font-family:Arial;
    line-height: 3ex;
}
code {
    display: inline-block;
    background: #fafafa;
    padding: 0 1ex;
}
#toggle {
    display:block;
    padding:10px;
    margin:10px auto;
    text-align:center;
    width:30ex;
}
#content {
    overflow:hidden;
    margin:10px;
    border:1px solid #666;
    background:#efefef;
    opacity:1;
}
#content .inner {
    padding:10px;
    overflow:auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<div id="content" class="open">
    <div class="inner">
        <h3>Smooth CSS Transitions Between <code>height: 0</code> and <code>height: auto</code></h3>
        <p>A clever workaround is to use <code>max-height</code> instead of <code>height</code>, and set it to something bigger than your content. Problem is the browser uses this value to calculate transition duration. So if you set it to <code>max-height: 1000px</code> but the content is only 100px high, the animation will be 10x too fast.</p>
        <p>Another option is to measure the content height with JS and transition to that fixed value, but then you have to keep track of the content and manually resize it if it changes.</p>
        <p>This solution is a hybrid of the two - transition to the measured content height, then set it to <code>max-height: 9999px</code> after the transition for fluid content sizing.</p>
    </div>
</div>

<br />

<button id="toggle">Challenge Accepted!</button>


14
@ Derija93 Đó là sử dụng hình ảnh động hết thời gian javascript cũ. Thay vào đó, thách thức là sử dụng các chuyển tiếp CSS3.
Adam

3
Vâng, vâng. Nhưng tại sao bạn lại sử dụng "chuyển tiếp CSS3" nếu trong ví dụ của bạn, đã sử dụng jQuery, một thư viện, cung cấp rất nhiều mã "viết ít hơn, làm nhiều hơn"? Tôi muốn chỉ ra rằng phiên bản của bạn có thể trông tốt hơn rất nhiều, mặc dù phức tạp hơn, nếu nó KHÔNG sử dụng jQuery để nó có thể chạy trên hầu như bất kỳ trang web nào. Tôi đoán tôi đã nói sai rất nhiều. Xin lỗi vì việc đó. ;)
Kiruse

27
@ Derija93 Có lẽ vì các chuyển đổi CSS3 có (theo tất cả các nguồn tôi có thể tìm thấy) hiệu suất tốt hơn nhiều so với hoạt ảnh jQuery. (Thực sự rất quan trọng đối với trường hợp sử dụng hiện tại của tôi, đã đưa tôi đến đây.)
Mark Amery

4
@ Derija93 'Vì hoạt ảnh Javascript chạy chậm so với chuyển đổi CSS3 bro và với hoạt ảnh jQuery, bạn phải lo lắng về vòng lặp hoạt hình. Làm sinh động một cái gì đó khi nhấp, sau đó nhấp nhanh và xem hoạt hình lặp lại. Điều này được xử lý với CSS3.
AlbertEngelB

2
@ Dropped.on.Caprica Bây giờ nó hơi cũ. Tôi đã nói rằng tôi đã hiểu nhầm khái niệm mà anh ta đang cố gắng thể hiện. Dù sao, đối với các hình ảnh động đơn giản, .stopthực hiện thủ thuật cho vấn đề vòng lặp hoạt hình khá phức tạp. Bây giờ khi bạn làm động chiều cao và màu sắc nhưng chỉ muốn làm gián đoạn hoạt hình chiều cao, mọi thứ trở nên phức tạp hơn một chút ... Tôi thực sự hiểu ý của bạn.
Kiruse

43

Câu trả lời được chấp nhận hoạt động trong hầu hết các trường hợp, nhưng nó không hoạt động tốt khi div chiều cao có thể thay đổi rất nhiều - tốc độ hoạt hình không phụ thuộc vào chiều cao thực tế của nội dung và nó có thể trông khó hiểu.

Bạn vẫn có thể thực hiện hoạt hình thực tế bằng CSS, nhưng bạn cần sử dụng JavaScript để tính chiều cao của các mục, thay vì cố gắng sử dụng auto. Không yêu cầu jQuery, mặc dù bạn có thể phải sửa đổi điều này một chút nếu bạn muốn tương thích (hoạt động trong phiên bản Chrome mới nhất :)).

window.toggleExpand = function(element) {
    if (!element.style.height || element.style.height == '0px') { 
        element.style.height = Array.prototype.reduce.call(element.childNodes, function(p, c) {return p + (c.offsetHeight || 0);}, 0) + 'px';
    } else {
        element.style.height = '0px';
    }
}
#menu #list {
    height: 0px;
    transition: height 0.3s ease;
    background: #d5d5d5;
    overflow: hidden;
}
<div id="menu">
    <input value="Toggle list" type="button" onclick="toggleExpand(document.getElementById('list'));">
    <ul id="list">
        <!-- Works well with dynamically-sized content. -->
        <li>item</li>
        <li><div style="height: 100px; width: 100px; background: red;"></div></li>
        <li>item</li>
        <li>item</li>
        <li>item</li>
    </ul>
</div>


Điều này có thể hữu ích, nhưng tôi không thể biết vì tôi không biết cách sử dụng nó mà không có ví dụ tốt hơn.
Ivan Durst

1
Bạn chỉ cần chuyển vào bất kỳ phần tử DOM nào (ví dụ: từ document.getElementById('mydiv')hoặc $('#mydiv').get()) và hàm chuyển đổi giữa ẩn / hiển thị nó. Nếu bạn thiết lập chuyển tiếp CSS, phần tử sẽ tự động hoạt động.
Oleg Vaskevich

Tôi đã thêm một đoạn. Điều này có lợi thế là làm việc tốt cho nội dung có kích thước động.
Oleg Vaskevich

3
-1, vì đây không phải là "sử dụng CSS". Điểm của câu hỏi là về một giải pháp không có JS. Tôi nghĩ rằng câu trả lời "chính xác", đó không phải là hack, mặc dù vậy ...
dudewad

16
Downvote cho việc sử dụng JS - nghiêm túc? Một nửa câu trả lời ở đây sử dụng JS, vì vậy downvote của bạn có vẻ không công bằng hoặc cần thiết. Bên cạnh đó, câu trả lời này vẫn đang sử dụng CSS để thực hiện hoạt hình thực tế, mà IMO là điểm của OP. Điều duy nhất mà JS được sử dụng là để tính chiều cao thực tế, vì không thể làm như vậy với CSS thuần túy (và cài đặt min-heightnhư trong câu trả lời dự kiến ​​sẽ gây ra vấn đề với tốc độ hoạt hình khi kích thước của danh sách có thể thay đổi lớn).
Oleg Vaskevich

30

Có rất ít đề cập đến thuộc Element.scrollHeighttính có thể hữu ích ở đây và vẫn có thể được sử dụng với chuyển đổi CSS thuần túy. Thuộc tính luôn chứa chiều cao "đầy đủ" của một phần tử, bất kể nội dung của nó có tràn ra như thế nào do kết quả của chiều cao bị thu gọn (ví dụ height: 0).

Như vậy, đối với height: 0phần tử (được thu gọn hoàn toàn), chiều cao "bình thường" hoặc "đầy đủ" của nó vẫn có sẵn thông qua scrollHeightgiá trị của nó (luôn luôn là chiều dài pixel ).

Đối với một yếu tố như vậy, giả sử nó đã có quá trình chuyển đổi được thiết lập như vd (sử dụng ultheo câu hỏi ban đầu):

ul {
    height: 0;
    transition: height 1s; /* An example transition. */
}

Chúng ta có thể kích hoạt "mở rộng" chiều cao hoạt hình mong muốn, chỉ sử dụng CSS, với một cái gì đó như sau (ở đây giả sử ulbiến đề cập đến danh sách):

ul.style.height = ul.scrollHeight + "px";

Đó là nó. Nếu bạn cần thu gọn danh sách, một trong hai câu sau sẽ làm:

ul.style.height = "0";
ul.style.removeProperty("height");

Trường hợp sử dụng cụ thể của tôi xoay quanh các danh sách hoạt hình có độ dài không xác định và thường có độ dài đáng kể, vì vậy tôi không cảm thấy thoải mái khi sử dụng tùy chọn "đủ lớn" heighthoặc max-heightđặc tả và có nguy cơ bị cắt nội dung hoặc nội dung mà bạn đột nhiên cần cuộn (nếu overflow: auto, chẳng hạn) . Ngoài ra, việc nới lỏng và thời gian bị phá vỡ với max-heightcác giải pháp dựa trên cơ sở, bởi vì chiều cao được sử dụng có thể đạt đến giá trị tối đa của nó sớm hơn rất nhiều so với mức cần thiết max-heightđể đạt được 9999px. Và khi độ phân giải màn hình tăng lên, độ dài pixel như 9999pxđể lại mùi vị khó chịu trong miệng tôi. Theo tôi, giải pháp đặc biệt này giải quyết vấn đề một cách tao nhã.

Cuối cùng, ở đây hy vọng rằng các bản sửa đổi trong tương lai của các tác giả địa chỉ CSS cần phải thực hiện những điều này một cách thanh lịch hơn - xem xét lại khái niệm giá trị "được tính" so với "đã sử dụng" và "đã giải quyết" và xem xét liệu chuyển đổi có nên áp dụng cho tính toán không các giá trị, bao gồm các chuyển tiếp với widthheight(hiện đang có một chút xử lý đặc biệt).


6
Bạn đã không tìm thấy nó trong các câu trả lời khác ở đây vì tương tác với DOM bằng thuộc Element.scrollHeighttính yêu cầu JavaScript và, như câu hỏi nêu rõ, họ muốn làm điều này mà không cần JavaScript.
TylerH

3
Có rất nhiều phần khác của CSS ngoài hai lớp giả có thể giải quyết vấn đề này, như các câu trả lời được đánh giá cao nhất trên trang 1 hiển thị. Đặt kiểu trong JavaScript không phải là "CSS thuần" vì nó yêu cầu phải đặt JS ở vị trí đầu tiên. Thông thường khi một người yêu cầu một giải pháp chỉ dành cho CSS, thì đó là vì họ không thể tiêm bất kỳ JavaScript nào hoặc không được phép bởi dự án hoặc vai trò hiện tại của họ.
TylerH

29

Sử dụng max-heightvới nới lỏng chuyển tiếp và trì hoãn khác nhau cho mỗi tiểu bang.

HTML:

<a href="#" id="trigger">Hover</a>
<ul id="toggled">
    <li>One</li>
    <li>Two</li>
    <li>Three</li>
<ul>

CSS:

#toggled{
    max-height: 0px;
    transition: max-height .8s cubic-bezier(0, 1, 0, 1) -.1s;
}

#trigger:hover + #toggled{
    max-height: 9999px;
    transition-timing-function: cubic-bezier(0.5, 0, 1, 0); 
    transition-delay: 0s;
}

Xem ví dụ: http://jsfiddle.net/0hnjehjc/1/


12
Vấn đề ở đây là, để đảm bảo bạn có đủ không gian trong môi trường năng động, bạn cần sử dụng chiều cao tối đa lố bịch như thế nào 999999px. Mặt khác, điều này sẽ thay đổi hình ảnh động của bạn, bởi vì các khung được tính từ giá trị này. Vì vậy, bạn có thể kết thúc với hoạt hình "trì hoãn" theo một hướng và kết thúc thực sự nhanh theo hướng khác (đúng: chức năng thời gian)
Julian F. Weinert

29

Không có giá trị mã hóa cứng.

Không có JavaScript.

Không có giá trị gần đúng.

Mẹo nhỏ là sử dụng ẩn & sao chép divđể trình duyệt hiểu được 100% nghĩa là gì.

Phương pháp này phù hợp bất cứ khi nào bạn có thể sao chép DOM của phần tử mà bạn muốn tạo hiệu ứng.

.outer {
  border: dashed red 1px;
  position: relative;
}

.dummy {
  visibility: hidden;
}

.real {
  position: absolute;
  background: yellow;
  height: 0;
  transition: height 0.5s;
  overflow: hidden;
}

.outer:hover>.real {
  height: 100%;
}
Hover over the box below:
<div class="outer">
  <!-- The actual element that you'd like to animate -->
  <div class="real">
unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable
content unpredictable content unpredictable content unpredictable content
  </div>
  <!-- An exact copy of the element you'd like to animate. -->
  <div class="dummy" aria-hidden="true">
unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable content unpredictable
content unpredictable content unpredictable content unpredictable content
  </div>
</div>


Hoàn thành tốt - đây là một giải pháp hợp lệ cho một vấn đề thực sự không nên tồn tại. Hoạt hình chiều cao tối đa là cơ sở trừ khi quá trình chuyển đổi được đặt thành "tuyến tính", điều đó cũng (rất rõ ràng) rất tệ đối với hoạt ảnh nội dung CMS trong đó chiều cao có thể thay đổi.
M_Willett

Điều này thật thông minh, nhưng tôi sẽ không thoải mái sao chép các yếu tố và nội dung DOM cho việc này.
Andre Figueiredo

Bạn có thể làm điều này mà không cần một thùng chứa và hiệu suất cao hơn bằng cách hoạt hình transform: scaleY(1), vì quá trình chuyển đổi này không loại bỏ không gian.
Fabian von Ellerts

21

Khi tôi đăng bài này, đã có hơn 30 câu trả lời, nhưng tôi cảm thấy câu trả lời của mình được cải thiện đối với câu trả lời đã được chấp nhận bởi jake.

Tôi không hài lòng với vấn đề phát sinh từ việc sử dụng đơn giản max-heightvà chuyển đổi CSS3, vì như nhiều người bình luận đã lưu ý, bạn phải đặt max-heightgiá trị của mình rất gần với chiều cao thực tế hoặc bạn sẽ bị chậm trễ. Xem JSFiddle này để biết ví dụ về vấn đề đó.

Để giải quyết vấn đề này (trong khi vẫn không sử dụng JavaScript), tôi đã thêm một yếu tố HTML khác chuyển đổi transform: translateYgiá trị CSS.

Điều này có nghĩa là cả hai max-heighttranslateYđược sử dụng: max-heightcho phép phần tử đẩy xuống các phần tử bên dưới nó, trong khi translateYmang lại hiệu ứng "tức thời" mà chúng ta muốn. Vấn đề với max-heightvẫn còn tồn tại, nhưng tác dụng của nó đã giảm đi. Điều này có nghĩa là bạn có thể đặt chiều cao lớn hơn nhiều cho max-heightgiá trị của mình và lo lắng về nó ít hơn.

Lợi ích tổng thể là khi chuyển đổi trở lại (sự sụp đổ), người dùng sẽ thấy translateYhoạt hình ngay lập tức, do đó không thực sự quan trọng là max-heightmất bao lâu .

Giải pháp như Fiddle

body {
  font-family: sans-serif;
}

.toggle {
  position: relative;
  border: 2px solid #333;
  border-radius: 3px;
  margin: 5px;
  width: 200px;
}

.toggle-header {
  margin: 0;
  padding: 10px;
  background-color: #333;
  color: white;
  text-align: center;
  cursor: pointer;
}

.toggle-height {
  background-color: tomato;
  overflow: hidden;
  transition: max-height .6s ease;
  max-height: 0;
}

.toggle:hover .toggle-height {
  max-height: 1000px;
}

.toggle-transform {
  padding: 5px;
  color: white;
  transition: transform .4s ease;
  transform: translateY(-100%);
}

.toggle:hover .toggle-transform {
  transform: translateY(0);
}
<div class="toggle">
  <div class="toggle-header">
    Toggle!
  </div>
  <div class="toggle-height">
    <div class="toggle-transform">
      <p>Content!</p>
      <p>Content!</p>
      <p>Content!</p>
      <p>Content!</p>
    </div>
  </div>
</div>

<div class="toggle">
  <div class="toggle-header">
    Toggle!
  </div>
  <div class="toggle-height">
    <div class="toggle-transform">
      <p>Content!</p>
      <p>Content!</p>
      <p>Content!</p>
      <p>Content!</p>
    </div>
  </div>
</div>


So sánh tốt đẹp @davidtaubmann, cây bút của bạn minh họa sự khác biệt rất tốt! Tôi đi với translateYhơn scalevì tôi không thích như thế nào scalecho rằng hiệu lực nén.
Andrew Messier

Cảm ơn @ andrew-messier, tôi đã xóa nhận xét vì có lỗi, dịchY không thực hiện trong codepen.io/davidtaubmann/pen/zKZvGP (vì không có đứa trẻ nào để di chuyển, cần sửa chữa) ... nhưng trong đó người ta hoàn toàn có thể thấy sự so sánh từ CHỈ MAX-HEIGHT và trộn nó với tỷ lệ (như đã nêu trong các câu trả lời khác): codepen.io/davidtaubmann/pen/jrdPER . Tất cả điều này đã giúp tôi rất nhiều với phương pháp Target mà tôi đang sử dụng
DavidTaubmann

18

Ok, vì vậy tôi nghĩ rằng tôi đã đưa ra một câu trả lời siêu đơn giản ... không max-height, sử dụng relativeđịnh vị, hoạt động trên licác yếu tố và là CSS thuần túy. Tôi chưa thử nghiệm bất cứ thứ gì ngoài Firefox, mặc dù đánh giá bằng CSS, nó sẽ hoạt động trên tất cả các trình duyệt.

FIDDLE: http://jsfiddle.net/n5XfG/2596/

CSS

.wrap { overflow:hidden; }

.inner {
            margin-top:-100%;
    -webkit-transition:margin-top 500ms;
            transition:margin-top 500ms;
}

.inner.open { margin-top:0px; }

HTML

<div class="wrap">
    <div class="inner">Some Cool Content</div>
</div>

13
Điều này sẽ làm việc cho đến khi chiều cao của phần tử vượt quá chiều rộng của nó. Đó là cơ sở của cách tính tỷ suất lợi nhuận bằng tỷ lệ phần trăm: chúng được tính dựa trên chiều rộng của phần tử. Vì vậy, nếu bạn có một phần tử rộng 1000 px, thì một phần tử ở 1100 px sẽ quá lớn để giải pháp này hoạt động, có nghĩa là bạn phải tăng tỷ lệ lợi nhuận âm đó. Về cơ bản, đó là vấn đề chính xác giống như sử dụng chiều cao hoặc chiều cao tối đa.
dudewad

15

EDIT: Cuộn xuống để trả lời cập nhật
Tôi đang tạo một danh sách thả xuống và thấy Bài đăng này ... nhiều câu trả lời khác nhau nhưng tôi quyết định chia sẻ danh sách thả xuống của mình, ... Nó không hoàn hảo nhưng ít nhất nó sẽ chỉ sử dụng css cho thả xuống Tôi đã sử dụng biến đổi: translateY (y) để chuyển đổi danh sách sang dạng xem ...
Bạn có thể xem thêm trong thử nghiệm
http://jsfiddle.net/BVEpc/4/
Tôi đã đặt div phía sau mỗi li vì danh sách thả xuống đang đến từ và để hiển thị chúng một cách chính xác điều này là cần thiết, mã div của tôi là:

#menu div {
    transition: 0.5s 1s;
    z-index:-1;
    -webkit-transform:translateY(-100%);
    -webkit-transform-origin: top;
}

và di chuột là:

#menu > li:hover div {
    transition: 0.5s;
    -webkit-transform:translateY(0);
}

và bởi vì chiều cao của ul được đặt thành nội dung, nó có thể vượt qua nội dung cơ thể của bạn, đó là lý do tại sao tôi làm điều này cho ul:

 #menu ul {
    transition: 0s 1.5s;
    visibility:hidden;
    overflow:hidden;
}

và di chuột:

#menu > li:hover ul {
     transition:none;
     visibility:visible;
}

lần thứ hai sau khi quá trình chuyển đổi bị trì hoãn và nó sẽ bị ẩn sau khi danh sách thả xuống của tôi được đóng lại một cách linh hoạt ...
Hy vọng sau này ai đó sẽ nhận được lợi ích của việc này.

EDIT: Tôi không thể tin ppl thực sự sử dụng nguyên mẫu này! menu thả xuống này chỉ dành cho một menu phụ và thế thôi !! Tôi đã cập nhật một cái tốt hơn có thể có hai menu phụ cho cả hướng ltr và rtl với hỗ trợ IE 8.
Fiddle cho LTR
Fiddle cho RTL
hy vọng ai đó sẽ thấy điều này hữu ích trong tương lai.


11

Bạn có thể chuyển từ chiều cao: 0 sang chiều cao: tự động cung cấp rằng bạn cũng cung cấp chiều cao tối thiểu và chiều cao tối đa.

div.stretchy{
    transition: 1s linear;
}

div.stretchy.hidden{
    height: 0;
}

div.stretchy.visible{
    height: auto;
    min-height:40px;
    max-height:400px;
}

@Stuart Cầu lông bạn có thể cung cấp ví dụ làm việc? Tôi đã kết hợp nó nhưng dường như nó không hoạt động ở bất cứ đâu :( jsfiddle.net/gryzzly/n5XfG
Misha Reyzlin

5
Vấn đề là với quy tắc chuyển đổi của bạn. để làm cho nó hoạt động, bạn cần áp dụng chuyển đổi sang chiều cao tối thiểu và tối đa. chuyển tiếp: chiều cao .5s tuyến tính, chiều cao tối thiểu .5s tuyến tính, chiều cao tối đa .5s tuyến tính
Cầu lông Stuart

2
Đây là những gì tôi đã có sau này, như bạn đã nói, cần thiết để tạo hiệu ứng chiều cao tối đa: jsfiddle.net/gryzzly/n5XfG/3
Misha Reyzlin

11
Tất nhiên, có một vấn đề với điều này. Thời gian cần thiết để tạo hiệu ứng chiều cao tối đa không phải là thời gian cần thiết để chuyển sang chiều cao tự động của hộp: nó đến ở độ cao: tự động trước, trước khi chiều cao tối đa kết thúc hoạt hình. Tương tự, khi chuyển sang 0, quá trình chuyển đổi không bắt đầu ngay lập tức, vì chiều cao tối đa bắt đầu ở độ cao lớn hơn chiều cao: tự động. Chuyển đổi sang chiều cao tối đa: 1000px cho thấy rõ điều này: jsfiddle.net/n5XfG/11
stephband

Bạn nói đúng, khi thử nghiệm thêm tôi đã nhận thấy điều này. Tôi không biết liệu chiều cao đầy đủ: 0 đến chiều cao: tự động có được thực hiện hay không nhưng sử dụng chiều cao tối đa trong một số trường hợp sẽ giải quyết tình huống không thể khác nếu chỉ sử dụng CSS.
Cầu lông Stuart

10

Giải pháp Flexbox

Ưu điểm:

  • đơn giản
  • không có JS
  • chuyển tiếp suôn sẻ

Nhược điểm:

  • yếu tố cần phải được đặt trong một container flex chiều cao cố định

Cách thức hoạt động của nó là luôn luôn có cơ sở flex: tự động dựa trên phần tử có nội dung, và chuyển đổi flex-grow và flex-co thay thế.

Chỉnh sửa: Cải thiện JS Fiddle lấy cảm hứng từ giao diện Xbox One.

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  transition: 0.25s;
  font-family: monospace;
}

body {
  margin: 10px 0 0 10px;
}

.box {
  width: 150px;
  height: 150px;
  margin: 0 2px 10px 0;
  background: #2d333b;
  border: solid 10px #20262e;
  overflow: hidden;
  display: inline-flex;
  flex-direction: column;
}

.space {
  flex-basis: 100%;
  flex-grow: 1;
  flex-shrink: 0;    
}

p {
  flex-basis: auto;
  flex-grow: 0;
  flex-shrink: 1;
  background: #20262e;
  padding: 10px;
  width: 100%;
  text-align: left;
  color: white;
}

.box:hover .space {
  flex-grow: 0;
  flex-shrink: 1;
}
  
.box:hover p {
  flex-grow: 1;
  flex-shrink: 0;    
}
<div class="box">
  <div class="space"></div>
  <p>
    Super Metroid Prime Fusion
  </p>
</div>
<div class="box">
  <div class="space"></div>
  <p>
    Resident Evil 2 Remake
  </p>
</div>
<div class="box">
  <div class="space"></div>
  <p>
    Yolo The Game
  </p>
</div>
<div class="box">
  <div class="space"></div>
  <p>
    Final Fantasy 7 Remake + All Additional DLC + Golden Tophat
  </p>
</div>
<div class="box">
  <div class="space"></div>
  <p>
    DerpVille
  </p>
</div>

Fiddle


7

Mở rộng câu trả lời của @ jake, quá trình chuyển đổi sẽ chuyển sang giá trị chiều cao tối đa, gây ra hiệu ứng động cực nhanh - nếu bạn đặt chuyển đổi cho cả hai: di chuột và tắt, bạn có thể điều khiển tốc độ điên cuồng thêm một chút.

Vì vậy, li: hover là khi chuột vào trạng thái và sau đó quá trình chuyển đổi trên thuộc tính không di chuột sẽ là chuột rời.

Hy vọng điều này sẽ có ích.

ví dụ:

.sidemenu li ul {
   max-height: 0px;
   -webkit-transition: all .3s ease;
   -moz-transition: all .3s ease;
   -o-transition: all .3s ease;
   -ms-transition: all .3s ease;
   transition: all .3s ease;
}
.sidemenu li:hover ul {
    max-height: 500px;
    -webkit-transition: all 1s ease;
   -moz-transition: all 1s ease;
   -o-transition: all 1s ease;
   -ms-transition: all 1s ease;
   transition: all 1s ease;
}
/* Adjust speeds to the possible height of the list */

Đây là một câu đố: http://jsfiddle.net/BukwJ/


1
Tôi không biết tại sao điều này có -1. Tôi thực sự nghĩ rằng đây là một nỗ lực tốt hơn so với một số người thêm vô số javascript. Nó không phải là một giải pháp hoàn hảo, nhưng với một số điều chỉnh, nó hoạt động tốt, tôi thấy nó hoạt động với giá trị trả về. chuyển tiếp: tất cả 500ms bezier (0,000, 1.225, 0.085, 0.385); Điều này dựa trên quá trình chuyển đổi 2 giây khi mở, sau đó mã ở trên để trở về trạng thái ban đầu. Cảm ơn ... Điều này đã giúp tôi tốt hơn tất cả những điều trên. +1 Tôi đã sử dụng trang web này để tạo ra bezier matthewlein.com/ceaser
gcoulby

OK nó mất một chút điều chỉnh. chuyển tiếp: tất cả 500ms khối-bezier (0.000, 1.390, 0.000, 0.635); Tôi nên xác định quá trình chuyển đổi của mình đi từ 5% -100% (nhưng thực tế giá trị cuối là khoảng 28%) vì vậy nó phụ thuộc vào dự án.
gcoulby

Tuyệt đối, nó không lý tưởng, nhưng nó là một sửa chữa nhanh chóng nếu bạn biết vòng tròn chiều cao trung bình sẽ là bao nhiêu.
jamie

7

Tôi nghĩ rằng tôi đã đưa ra một giải pháp thực sự vững chắc

ĐỒNG Ý! Tôi biết vấn đề này cũng lâu đời như internet nhưng tôi nghĩ rằng tôi có một giải pháp mà tôi đã biến thành một plugin gọi là chuyển đổi đột biến . Giải pháp của tôi đặt các style=""thuộc tính cho các phần tử được theo dõi bất cứ khi nào có thay đổi trong DOM. kết quả cuối cùng là bạn có thể sử dụng CSS ole tốt cho các chuyển đổi của mình và không sử dụng các bản sửa lỗi hacky hoặc javascript đặc biệt. Điều duy nhất bạn phải làm là thiết lập những gì bạn muốn theo dõi trên phần tử được đề cập bằng cách sử dụng data-mutant-attributes="X".

<div data-mutant-attributes="height">                                                                      
        This is an example with mutant-transition                                                                                                          
    </div>

Đó là nó! Giải pháp này sử dụng M mutObserver để theo dõi các thay đổi trong DOM. Vì điều này, bạn thực sự không phải thiết lập bất cứ điều gì hoặc sử dụng javascript để làm động các thứ thủ công. Thay đổi được theo dõi tự động. Tuy nhiên, vì nó sử dụng MutingObserver, điều này sẽ chỉ chuyển tiếp trong IE11 +.

Câu đố!


12
Thông thường, khi ai đó hỏi cách làm một cái gì đó "không có JavaScript", điều đó có nghĩa là giải pháp phải hoạt động trên các trình duyệt đã tắt JavaScript. Nó không có nghĩa là "không viết kịch bản của riêng tôi". Vì vậy, để nói rằng plugin của bạn có thể được sử dụng mà không sử dụng JavaScript là sai lệch nhất - coi đó là plugin JavaScript.
BoltClock

Tôi nên làm rõ, khi tôi nói bạn không phải sử dụng bất kỳ javascript đặc biệt nào, ý tôi là bạn không phải viết bất kỳ javascript nào. chỉ bao gồm thư viện JS và chỉ định các thuộc tính bạn muốn xem trong HTML. Bạn không phải sử dụng css chiều cao cố định, hoặc tìm ra bất cứ điều gì. Chỉ cần phong cách và đi.
JonTroncoso

"(...) Bạn không thực sự phải thiết lập bất cứ điều gì hoặc sử dụng javascript để tạo hiệu ứng động mọi thứ (...)" vì vậy bạn đang sử dụng JavaScript để giải trí
Roko C. Buljan

6

Đây là một cách để chuyển từ bất kỳ chiều cao bắt đầu nào, bao gồm 0, sang tự động (kích thước đầy đủ và linh hoạt) mà không yêu cầu mã cứng trên cơ sở mỗi nút hoặc bất kỳ mã người dùng nào để khởi tạo: https://github.com/csuwildcat / quá trình tự động . Về cơ bản, đây là chén thánh cho những gì bạn muốn, tôi tin rằng -> http://codepen.io/csuwldcat/pen/kwsdF . Chỉ cần đưa tệp JS sau vào trang của bạn và tất cả những gì bạn cần làm sau đó là thêm / xóa một thuộc tính boolean duy nhất - reveal=""- từ các nút bạn muốn mở rộng và ký hợp đồng.

Đây là tất cả những gì bạn cần làm với tư cách là người dùng, một khi bạn bao gồm khối mã được tìm thấy bên dưới mã ví dụ:

/*** Nothing out of the ordinary in your styles ***/
<style>
    div {
        height: 0;
        overflow: hidden;
        transition: height 1s;
    }
</style>

/*** Just add and remove one attribute and transition to/from auto! ***/

<div>
    I have tons of content and I am 0px in height you can't see me...
</div>

<div reveal>
     I have tons of content and I am 0px in height you can't see me...
     but now that you added the 'reveal' attribute, 
     I magically transitioned to full height!...
</div>

Đây là khối mã để đưa vào trang của bạn, sau đó, tất cả đều hấp dẫn:

Thả tệp JS này vào trang của bạn - tất cả chỉ là hoạt động ™

/ * Mã cho chiều cao: tự động; đang chuyển tiếp * /

(function(doc){

/* feature detection for browsers that report different values for scrollHeight when an element's overflow is hidden vs visible (Firefox, IE) */
var test = doc.documentElement.appendChild(doc.createElement('x-reveal-test'));
    test.innerHTML = '-';
    test.style.cssText = 'display: block !important; height: 0px !important; padding: 0px !important; font-size: 0px !important; border-width: 0px !important; line-height: 1px !important; overflow: hidden !important;';
var scroll = test.scrollHeight || 2;
doc.documentElement.removeChild(test);

var loading = true,
    numReg = /^([0-9]*\.?[0-9]*)(.*)/,
    skipFrame = function(fn){
      requestAnimationFrame(function(){
        requestAnimationFrame(fn);
      });
    },
    /* 2 out of 3 uses of this function are purely to work around Chrome's catastrophically busted implementation of auto value CSS transitioning */
    revealFrame = function(el, state, height){
        el.setAttribute('reveal-transition', 'frame');
        el.style.height = height;
        skipFrame(function(){
            el.setAttribute('reveal-transition', state);
            el.style.height = '';
        });
    },
    transitionend = function(e){
      var node = e.target;
      if (node.hasAttribute('reveal')) {
        if (node.getAttribute('reveal-transition') == 'running') revealFrame(node, 'complete', '');
      } 
      else {
        node.removeAttribute('reveal-transition');
        node.style.height = '';
      }
    },
    animationstart = function(e){
      var node = e.target,
          name = e.animationName;   
      if (name == 'reveal' || name == 'unreveal') {

        if (loading) return revealFrame(node, 'complete', 'auto');

        var style = getComputedStyle(node),
            offset = (Number(style.paddingTop.match(numReg)[1])) +
                     (Number(style.paddingBottom.match(numReg)[1])) +
                     (Number(style.borderTopWidth.match(numReg)[1])) +
                     (Number(style.borderBottomWidth.match(numReg)[1]));

        if (name == 'reveal'){
          node.setAttribute('reveal-transition', 'running');
          node.style.height = node.scrollHeight - (offset / scroll) + 'px';
        }
        else {
            if (node.getAttribute('reveal-transition') == 'running') node.style.height = '';
            else revealFrame(node, 'running', node.scrollHeight - offset + 'px');
        }
      }
    };

doc.addEventListener('animationstart', animationstart, false);
doc.addEventListener('MSAnimationStart', animationstart, false);
doc.addEventListener('webkitAnimationStart', animationstart, false);
doc.addEventListener('transitionend', transitionend, false);
doc.addEventListener('MSTransitionEnd', transitionend, false);
doc.addEventListener('webkitTransitionEnd', transitionend, false);

/*
    Batshit readyState/DOMContentLoaded code to dance around Webkit/Chrome animation auto-run weirdness on initial page load.
    If they fixed their code, you could just check for if(doc.readyState != 'complete') in animationstart's if(loading) check
*/
if (document.readyState == 'complete') {
    skipFrame(function(){
        loading = false;
    });
}
else document.addEventListener('DOMContentLoaded', function(e){
    skipFrame(function(){
        loading = false;
    });
}, false);

/* Styles that allow for 'reveal' attribute triggers */
var styles = doc.createElement('style'),
    t = 'transition: none; ',
    au = 'animation: reveal 0.001s; ',
    ar = 'animation: unreveal 0.001s; ',
    clip = ' { from { opacity: 0; } to { opacity: 1; } }',
    r = 'keyframes reveal' + clip,
    u = 'keyframes unreveal' + clip;

styles.textContent = '[reveal] { -ms-'+ au + '-webkit-'+ au +'-moz-'+ au + au +'}' +
    '[reveal-transition="frame"] { -ms-' + t + '-webkit-' + t + '-moz-' + t + t + 'height: auto; }' +
    '[reveal-transition="complete"] { height: auto; }' +
    '[reveal-transition]:not([reveal]) { -webkit-'+ ar +'-moz-'+ ar + ar +'}' +
    '@-ms-' + r + '@-webkit-' + r + '@-moz-' + r + r +
    '@-ms-' + u +'@-webkit-' + u + '@-moz-' + u + u;

doc.querySelector('head').appendChild(styles);

})(document);

/ * Mã cho DEMO * /

    document.addEventListener('click', function(e){
      if (e.target.nodeName == 'BUTTON') {
        var next = e.target.nextElementSibling;
        next.hasAttribute('reveal') ? next.removeAttribute('reveal') : next.setAttribute('reveal', '');
      }
    }, false);

9
Mặc dù đây là một giải pháp linh hoạt và gọn gàng hơn nhiều so với các giải pháp khác đã được đề xuất, cá nhân tôi tin rằng đây không phải là thứ mà JS nên chạm vào. Không nói bạn sai, đó chỉ là một tiếng thở dài.
mystrdat

@mystrdat để rõ ràng, mặc dù, JS chỉ được sử dụng để theo dõi một số điều kiện nhất định và thêm một vài thuộc tính / giá trị CSS tạm thời dựa trên các sự kiện / điều kiện mà nó đang theo dõi. Quá trình chuyển đổi vẫn hoàn thành trong CSS. Mặc dù tôi đồng ý, nhưng thật đáng buồn khi mức độ thiết lập và cầm tay này là cần thiết để thực sự thỏa mãn một trường hợp sử dụng có vẻ đơn giản! : /
csuwldcat

1
@KarolisRamanauskas không phải là vấn đề - IE hỗ trợ cả CSS Keyframes và requestAnimationFrame. Tôi đã sử dụng clipnhư trình kích hoạt thuộc tính giả của mình và vì một số lý do, IE đã ngừng cho phép clip hoạt hình. Tôi chuyển sang opacity và tất cả hoạt động trở lại: codepen.io/csuwldcat/pen/FpzGa . Tôi đã cập nhật mã trong câu trả lời cho phù hợp.
csuwldcat

1
@csuwldcat Giải pháp rất hay, tôi nhận thấy rằng nội dung xuất hiện và biến mất trong một phần nhỏ, tại sao điều này xảy ra? Có tránh được không?
Beto Aveiga

12
Tôi muốn nâng cao điều này, nhưng thay vì trả lời câu hỏi làm thế nào để nó hoạt động, bạn đã chia sẻ một plugin mà bạn đã viết để làm cho nó hoạt động. Chúng tôi, những người tò mò, còn lại để thiết kế đảo ngược plugin của bạn, điều này không thú vị lắm. Tôi ước bạn sẽ cập nhật câu trả lời của mình để có thêm lời giải thích về những gì plugin của bạn làm và tại sao. Thay đổi mã để được giải thích nhiều hơn. Ví dụ, bạn có cả một đoạn mã chỉ viết CSS tĩnh. Tôi thà thấy CSS, hơn là mã tạo ra nó. Bạn có thể bỏ qua các phần nhàm chán, như lặp lại cho tất cả các tiền tố trình duyệt.
gilly3

6

Câu trả lời của Jake để tạo hiệu ứng cho chiều cao tối đa là rất tốt, nhưng tôi thấy độ trễ gây ra bằng cách đặt độ cao tối đa lớn gây khó chịu.

Người ta có thể di chuyển nội dung có thể thu gọn vào một div bên trong và tính chiều cao tối đa bằng cách lấy chiều cao của div bên trong (thông qua JQuery, nó sẽ là bên ngoài ()).

$('button').bind('click', function(e) { 
  e.preventDefault();
  w = $('#outer');
  if (w.hasClass('collapsed')) {
    w.css({ "max-height": $('#inner').outerHeight() + 'px' });
  } else {
    w.css({ "max-height": "0px" });
  }
  w.toggleClass('collapsed');
});

Đây là một liên kết jsfiddle: http://jsfiddle.net/pbatey/duZpT

Đây là một jsfiddle với số lượng mã tối thiểu tuyệt đối cần có: http://jsfiddle.net/8ncjjxh8/


5

Tôi nhận ra chủ đề này đang trở nên cũ, nhưng nó được xếp hạng cao trên một số tìm kiếm Google nhất định vì vậy tôi cho rằng nó đáng để cập nhật.

Bạn cũng chỉ cần lấy / đặt chiều cao của phần tử:

var load_height = document.getElementById('target_box').clientHeight;
document.getElementById('target_box').style.height = load_height + 'px';

Bạn nên kết xuất Javascript này ngay sau thẻ đóng của target_box trong thẻ script nội tuyến.


document.getEuityById ('target_box'). getBoundingClientRect (). chiều cao nên làm tốt nhất.
thay đổi

5

Tôi đã có thể làm điều này. Tôi có một .child& một .parentdiv. Div con phù hợp hoàn hảo trong chiều rộng / chiều cao của cha mẹ với absoluteđịnh vị. Sau đó, tôi làm động translatetài sản để đẩy Ygiá trị của nó xuống 100%. Hoạt hình rất mượt mà của nó, không có trục trặc hoặc xuống dưới như bất kỳ giải pháp nào khác ở đây.

Một cái gì đó như thế này, mã giả

.parent{ position:relative; overflow:hidden; } 
/** shown state */
.child {
  position:absolute;top:0;:left:0;right:0;bottom:0;
  height: 100%;
  transition: transform @overlay-animation-duration ease-in-out;
  .translate(0, 0);
}

/** Animate to hidden by sliding down: */
.child.slidedown {
  .translate(0, 100%); /** Translate the element "out" the bottom of it's .scene container "mask" so its hidden */
}

Bạn sẽ chỉ định một heighttrên .parent, trong px, %hoặc nghỉ như auto. Div này sau đó che dấu .childdiv khi nó trượt xuống.


Ví dụ làm việc này sẽ được nhiều đánh giá cao.
Chất dẫn truyền thần kinh

5

Tôi đã đăng một câu trả lời với một số JavaScript và bị đánh giá thấp, vì vậy đã cảm thấy khó chịu và thử lại và đã bẻ khóa nó chỉ bằng CSS!

Giải pháp này sử dụng một vài "kỹ thuật":

  • padding-bottom:100%'Hack' trong đó tỷ lệ phần trăm được xác định theo độ rộng hiện tại của phần tử. Thông tin thêm về kỹ thuật này.
  • float co-quấn, (bắt buộc phải có thêm div để áp dụng hack float float)
  • việc sử dụng https://caniuse.com/#feat=css-wr-mode và một số biến đổi để hoàn tác nó một cách không đáng kể (điều này cho phép sử dụng hack pad ở trên trong ngữ cảnh dọc)

Mặc dù vậy, kết quả cuối cùng là chúng ta chỉ chuyển đổi hiệu suất bằng cách sử dụng CSS và một chức năng chuyển tiếp duy nhất để đạt được sự chuyển đổi suôn sẻ; chén Thánh!

Tất nhiên, có một nhược điểm! Tôi không thể tìm ra cách kiểm soát độ rộng mà nội dung bị cắt ( overflow:hidden); Do hack dưới đáy, chiều rộng và chiều cao có liên quan mật thiết với nhau. Có thể có một cách mặc dù, vì vậy sẽ trở lại với nó.

https://jsfiddle.net/EoghanM/n1rp3zb4/28/

body {
  padding: 1em;
}

.trigger {
  font-weight: bold;
}

/* .expander is there for float clearing purposes only */
.expander::after {
  content: '';
  display: table;
  clear: both;
}

.outer {
  float: left; /* purpose: shrink to fit content */
  border: 1px solid green;
  overflow: hidden;
}

.inner {
  transition: padding-bottom 0.3s ease-in-out;  /* or whatever crazy transition function you can come up with! */
  padding-bottom: 0%;  /* percentage padding is defined in terms of width. The width at this level is equal to the height of the content */
  height: 0;

  /* unfortunately, change of writing mode has other bad effects like orientation of cursor */
  writing-mode: vertical-rl;
  cursor: default; /* don't want the vertical-text (sideways I-beam) */
  transform: rotate(-90deg) translateX(-100%);  /* undo writing mode */
  transform-origin: 0 0;
  margin: 0;  /* left/right margins here will add to height */
}

.inner > div { white-space: nowrap; }

.expander:hover .inner,  /* to keep open when expanded */
.trigger:hover+.expander .inner {
  padding-bottom: 100%;
}
<div class="trigger">HoverMe</div>
<div class="expander">
  <div class="outer">
    <div class="inner">
      <div>First Item</div>
      <div>Content</div>
      <div>Content</div>
      <div>Content</div>
      <div>Long Content can't be wider than outer height unfortunately</div>
      <div>Last Item</div>
    </div>
  </div>
</div>
<div>
  after content</div>
</div>


Nhược điểm khác của điều này là bạn không thể di chuyển con trỏ khỏi div "hoverme" vào danh sách các mục để tương tác với chúng (rõ ràng là lý do để OP hỏi), vì vậy nó chỉ hữu ích như một hàm tooltip chứ không phải , nói, một menu với các menu con lồng nhau.
TylerH

Đó hoàn toàn là hậu quả của cách đặt câu hỏi và không liên quan đến kỹ thuật. Bạn có thể thay đổi trình kích hoạt thành một input[type="checkbox"]:checkedthay vì :hovernếu bạn muốn (tức là bạn không muốn sử dụng JavaScript để thêm bất kỳ lớp nào)
EoghanM

Ý tôi là, bất kể câu hỏi được xây dựng như thế nào, nếu việc triển khai một giải pháp bị hạn chế như vậy, có lẽ nên được đề cập trong câu trả lời của giải pháp đó (hoặc tốt nhất là được tính đến ). Bạn đề cập đến giải pháp này là một 'chén thánh' nhưng div chuyển đổi thậm chí không thể được di chuột qua ... dường như không phải là một giải pháp tối ưu đối với tôi.
TylerH

Xin chào @TylerH, xin lỗi, tôi đã bỏ lỡ thực tế là trình kích hoạt trong câu hỏi ban đầu bao quanh danh sách mở rộng, vì vậy danh sách sẽ vẫn mở khi di chuột qua. Tôi đã cập nhật câu trả lời của mình để phản ánh điều này. Như tôi đã đề cập, cách nó được kích hoạt không phải là tất cả, vì tôi đang trình bày một kỹ thuật mới mà trước đây chỉ có thể nghĩ là có thể có trong CSS (Tôi đã tìm kiếm một giải pháp trong vài năm).
EoghanM

4

Đây là một giải pháp tôi vừa sử dụng kết hợp với jQuery. Điều này hoạt động cho cấu trúc HTML sau đây:

<nav id="main-nav">
    <ul>
        <li>
            <a class="main-link" href="yourlink.html">Link</a>
            <ul>
                <li><a href="yourlink.html">Sub Link</a></li>
            </ul>
        </li>
    </ul>
</nav>

và chức năng:

    $('#main-nav li ul').each(function(){
        $me = $(this);

        //Count the number of li elements in this UL
        var liCount = $me.find('li').size(),
        //Multiply the liCount by the height + the margin on each li
            ulHeight = liCount * 28;

        //Store height in the data-height attribute in the UL
        $me.attr("data-height", ulHeight);
    });

Sau đó, bạn có thể sử dụng chức năng nhấp để đặt và xóa chiều cao bằng cách sử dụng css()

$('#main-nav li a.main-link').click(function(){
    //Collapse all submenus back to 0
    $('#main-nav li ul').removeAttr('style');

    $(this).parent().addClass('current');

    //Set height on current submenu to it's height
    var $currentUl = $('li.current ul'),
        currentUlHeight = $currentUl.attr('data-height');
})

CSS:

#main-nav li ul { 
    height: 0;
    position: relative;
    overflow: hidden;
    opacity: 0; 
    filter: alpha(opacity=0); 
    -ms-filter: "alpha(opacity=0)";
    -khtml-opacity: 0; 
    -moz-opacity: 0;
    -webkit-transition: all .6s ease-in-out;
    -moz-transition: all .6s ease-in-out;
    -o-transition: all .6s ease-in-out;
    -ms-transition: all .6s ease-in-out;
    transition: all .6s ease-in-out;
}

#main-nav li.current ul {
    opacity: 1.0; 
    filter: alpha(opacity=100); 
    -ms-filter: "alpha(opacity=100)";
    -khtml-opacity: 1.0; 
    -moz-opacity: 1.0;
}

.ie #main-nav li.current ul { height: auto !important }

#main-nav li { height: 25px; display: block; margin-bottom: 3px }

điều này không hiển thị đúng với phiên bản jquery mới nhất
oliverbachmann

4

Gần đây tôi đã chuyển đổi max-heightcác liyếu tố chứ không phải là gói ul.

Lý do là độ trễ cho nhỏ max-heightsít đáng chú ý hơn (nếu có) so với lớn max-heights, và tôi cũng có thể đặt max-heightgiá trị của mình so với font-sizesố lithay vì một số lớn tùy ý bằng cách sử dụng emshoặc rems.

Nếu kích thước phông chữ của tôi là 1rem, tôi sẽ đặt tôi max-heightthành một cái gì đó như 3rem(để chứa văn bản được bao bọc). Bạn có thể xem một ví dụ ở đây:

http://codepen.io/mindfullsilence/pen/DtzjE


3

Tôi chưa đọc mọi thứ chi tiết nhưng tôi đã gặp vấn đề này gần đây và tôi đã làm như sau:

div.class{
   min-height:1%;
   max-height:200px;
   -webkit-transition: all 0.5s ease;
   -moz-transition: all 0.5s ease;
   -o-transition: all 0.5s ease;
   -webkit-transition: all 0.5s ease;
   transition: all 0.5s ease;
   overflow:hidden;
}

div.class:hover{
   min-height:100%;
   max-height:3000px;
}

Điều này cho phép bạn có một div mà lúc đầu hiển thị nội dung có chiều cao lên tới 200px và khi di chuột, kích thước của nó trở nên ít nhất bằng toàn bộ nội dung của div. Div không trở thành 3000px nhưng 3000px là giới hạn mà tôi đang áp đặt. Đảm bảo có quá trình chuyển đổi trên non: hover, nếu không bạn có thể nhận được một số kết xuất lạ. Theo cách này, hover: kế thừa từ non: hover.

Chuyển đổi không hoạt động từ px đến% hoặc tự động. Bạn cần sử dụng cùng một đơn vị đo lường. Cái này làm việc tốt cho tôi. Sử dụng HTML5 làm cho nó hoàn hảo ....

Hãy nhớ rằng luôn có một công việc xung quanh ...; )

Hy vọng ai đó thấy điều này hữu ích


3

Giải pháp chiều cao tối đa từ Jake hoạt động tốt, nếu giá trị chiều cao tối đa được mã hóa cứng được cung cấp không lớn hơn nhiều so với chiều cao thực (vì nếu không có sự chậm trễ không mong muốn và các vấn đề về thời gian). Mặt khác, nếu giá trị mã hóa cứng ngẫu nhiên không lớn hơn chiều cao thực thì phần tử sẽ không mở hoàn toàn.

Giải pháp chỉ CSS sau đây cũng yêu cầu kích thước được mã hóa cứng phải lớn hơn hầu hết các kích thước thực xảy ra. Tuy nhiên, giải pháp này cũng hoạt động nếu kích thước thực trong một số trường hợp lớn hơn kích thước được mã hóa cứng. Trong trường hợp đó, quá trình chuyển đổi có thể nhảy một chút, nhưng nó sẽ không bao giờ để lại một phần tử hiển thị. Vì vậy, giải pháp này cũng có thể được sử dụng cho nội dung không xác định, ví dụ từ cơ sở dữ liệu, nơi bạn chỉ biết rằng nội dung thường không lớn hơn x pixel, nhưng vẫn có ngoại lệ.

Ý tưởng là sử dụng một giá trị âm cho đáy-lề (hoặc lề-trên cho hoạt hình hơi khác nhau) và đặt phần tử nội dung vào một phần tử ở giữa có tràn: ẩn. Lề âm của phần tử nội dung vì vậy làm giảm chiều cao của phần tử ở giữa.

Đoạn mã sau sử dụng chuyển đổi trên lề dưới từ -150px sang 0px. Điều này một mình hoạt động tốt miễn là yếu tố nội dung không cao hơn 150px. Ngoài ra, nó sử dụng chuyển đổi trên chiều cao tối đa cho phần tử ở giữa từ 0px đến 100%. Điều này cuối cùng ẩn phần tử ở giữa nếu phần tử nội dung cao hơn 150px. Đối với chiều cao tối đa, quá trình chuyển đổi chỉ được sử dụng để trì hoãn ứng dụng của nó trong một giây khi đóng, không cho hiệu ứng hiển thị mượt mà (và do đó nó có thể chạy từ 0px đến 100%).

CSS:

.content {
  transition: margin-bottom 1s ease-in;
  margin-bottom: -150px;
}
.outer:hover .middle .content {
  transition: margin-bottom 1s ease-out;
  margin-bottom: 0px
}
.middle {
  overflow: hidden;
  transition: max-height .1s ease 1s;
  max-height: 0px
}
.outer:hover .middle {
  transition: max-height .1s ease 0s;
  max-height: 100%
}

HTML:

<div class="outer">
  <div class="middle">
    <div class="content">
      Sample Text
      <br> Sample Text
      <br> Sample Text
      <div style="height:150px">Sample Test of height 150px</div>
      Sample Text
    </div>
  </div>
  Hover Here
</div>

Giá trị cho đáy lề phải âm và càng gần càng tốt với chiều cao thực của phần tử nội dung. Nếu giá trị absoute của nó lớn hơn thì sẽ có các vấn đề về độ trễ và thời gian tương tự như với các giải pháp chiều cao tối đa, tuy nhiên có thể bị giới hạn miễn là kích thước được mã hóa cứng không lớn hơn nhiều so với thực tế. Nếu giá trị tuyệt đối cho lề dưới nhỏ hơn chiều cao thực, thì độ nghiêng nhảy một chút. Trong mọi trường hợp sau khi chuyển đổi, phần tử nội dung được hiển thị đầy đủ hoặc loại bỏ hoàn toàn.

Để biết thêm chi tiết, hãy xem bài đăng trên blog của tôi http://www.taccgl.org/blog/css_transition_display.html#combined_height


3

Bạn có thể làm điều này bằng cách tạo một hình ảnh động (thu gọn) với đường dẫn clip.

#child0 {
    display: none;
}
#parent0:hover #child0 {
    display: block;
    animation: height-animation;
    animation-duration: 200ms;
    animation-timing-function: linear;
    animation-fill-mode: backwards;
    animation-iteration-count: 1;
    animation-delay: 200ms;
}
@keyframes height-animation {
    0% {
        clip-path: polygon(0% 0%, 100% 0.00%, 100% 0%, 0% 0%);
    }
    100% {
        clip-path: polygon(0% 0%, 100% 0.00%, 100% 100%, 0% 100%);
    }
}
<div id="parent0">
    <h1>Hover me (height: 0)</h1>
    <div id="child0">Some content
        <br>Some content
        <br>Some content
        <br>Some content
        <br>Some content
        <br>Some content
        <br>
    </div>
</div>


2

Đây không chính xác là một "giải pháp" cho vấn đề, mà là một cách giải quyết khác. Nó chỉ hoạt động như được viết bằng văn bản, nhưng có thể thay đổi để hoạt động với các yếu tố khác khi cần Tôi chắc chắn.

.originalContent {
    font-size:0px;
    transition:font-size .2s ease-in-out;
}
.show { /* class to add to content */
    font-size:14px;
}

Dưới đây là một ví dụ: http://codepen.io/overthemike/pen/wzjRKa

Về cơ bản, bạn đặt kích thước phông chữ thành 0 và chuyển đổi thay vì chiều cao, chiều cao tối đa hoặc tỷ lệY (), v.v ... với tốc độ đủ nhanh để có được chiều cao để chuyển đổi theo chiều cao bạn muốn. Để chuyển đổi chiều cao thực tế bằng CSS thành tự động hiện không thể thực hiện được, nhưng chuyển đổi nội dung bên trong là do đó chuyển đổi kích thước phông chữ.

  • Lưu ý - có javascript trong codepen, nhưng mục đích duy nhất của nó là thêm / xóa các lớp css khi nhấp cho accordion. Điều này có thể được thực hiện với các nút radio ẩn, nhưng tôi không tập trung vào đó, chỉ là chuyển đổi chiều cao.

1
Hiệu quả sao chép câu trả lời này , nhưng bỏ qua hiệu ứng mờ dần.
SeldomNeedy

Đã đồng ý. Cách tiếp cận tốt hơn.
Mike S.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.