मुझे पता है कि यह इस सवाल का तीसवाँ जवाब है, लेकिन मुझे लगता है कि यह इसके लायक है, इसलिए यहाँ जाता है। यह निम्नलिखित गुणों के साथ CSS-only समाधान है:
- शुरुआत में देरी नहीं होती है, और संक्रमण जल्दी नहीं रुकता है। दोनों दिशाओं में (विस्तार और पतन), यदि आप अपने CSS में 300ms की संक्रमण अवधि निर्दिष्ट करते हैं, तो संक्रमण 300ms, अवधि लेता है।
- यह वास्तविक ऊँचाई (विपरीत
transform: scaleY(0)
) का संक्रमण कर रहा है , इसलिए यह सही काम करता है अगर बंधनेवाला तत्व के बाद सामग्री हो।
- जबकि (अन्य समाधानों की तरह) में मैजिक नंबर होते हैं (जैसे "एक लंबाई जो आपके बॉक्स की तुलना में अधिक है जो कभी भी होने जा रही है"), यह गलत नहीं है यदि आपकी धारणा गलत हो जाती है। संक्रमण उस मामले में आश्चर्यजनक नहीं लग सकता है, लेकिन संक्रमण से पहले और बाद में, यह कोई समस्या नहीं है: विस्तारित (
height: auto
) स्थिति में, पूरी सामग्री में हमेशा सही ऊँचाई होती है (इसके विपरीत यदि आप उठाते हैं तो max-height
वह बाहर हो जाती है बहुत कम)। और ध्वस्त अवस्था में, ऊँचाई शून्य होनी चाहिए जैसी कि होनी चाहिए।
डेमो
यहां तीन संक्षिप्त तत्वों के साथ एक डेमो है, सभी अलग-अलग ऊंचाइयों पर हैं, जो सभी एक ही सीएसएस का उपयोग करते हैं। आप "रन स्निपेट" पर क्लिक करने के बाद "पूर्ण पृष्ठ" पर क्लिक करना चाह सकते हैं। ध्यान दें कि जावास्क्रिप्ट केवल collapsed
CSS वर्ग को जन्म देता है , इसमें कोई माप शामिल नहीं है। (आप किसी चेकबॉक्स का उपयोग करके या किसी भी जावास्क्रिप्ट के बिना यह सटीक डेमो कर सकते हैं :target
)। यह भी ध्यान दें कि संक्रमण के लिए जिम्मेदार सीएसएस का हिस्सा बहुत छोटा है, और HTML को केवल एक अतिरिक्त आवरण तत्व की आवश्यकता है।
$(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>
यह कैसे काम करता है?
वास्तव में ऐसा होने में दो संक्रमण शामिल हैं। उनमें से एक ढह गई स्थिति margin-bottom
में 0px (विस्तारित अवस्था में) से संक्रमण करता है ( इस उत्तर के-2000px
समान )। 2000 यहाँ पहली जादुई संख्या है, यह इस धारणा पर आधारित है कि आपका बॉक्स इससे अधिक नहीं होगा (2000 पिक्सेल एक उचित विकल्प की तरह लगता है)।
margin-bottom
अकेले संक्रमण द्वारा दो मुद्दों का उपयोग करना :
- यदि आपके पास वास्तव में एक बॉक्स है जो 2000 पिक्सेल से अधिक है, तो
margin-bottom: -2000px
सब कुछ नहीं छिपाएगा - ढह गए मामले में भी सामान दिखाई देगा। यह एक मामूली सुधार है जिसे हम बाद में करेंगे।
- यदि वास्तविक बॉक्स, 1000 पिक्सेल ऊंचा है, और आपका संक्रमण 300ms लंबा है, तो दृश्यमान संक्रमण लगभग 150ms (या, विपरीत दिशा में, 150ms देर से शुरू होता है) के बाद पहले से ही खत्म हो गया है।
इस दूसरे मुद्दे को ठीक करना वह जगह है जहाँ दूसरा संक्रमण आता है, और यह संक्रमण अवधारणात्मक रूप से आवरण की न्यूनतम ऊँचाई को लक्षित करता है ("वैचारिक रूप से" क्योंकि हम वास्तव में इसके लिए min-height
संपत्ति का उपयोग नहीं कर रहे हैं; उस पर और बाद में)।
यहां एक एनीमेशन है जो दिखाता है कि न्यूनतम ऊंचाई संक्रमण के साथ नीचे के मार्जिन संक्रमण को कैसे जोड़ा जाए, दोनों समान अवधि में, हमें पूर्ण ऊंचाई से शून्य ऊंचाई तक एक संयुक्त संक्रमण देता है जिसमें समान अवधि होती है।
बाएं पट्टी से पता चलता है कि नकारात्मक ऊंचाई मार्जिन नीचे की ओर कैसे ऊपर की ओर धकेलती है, दृश्यमान ऊंचाई को कम करती है। मध्य पट्टी दिखाती है कि न्यूनतम ऊंचाई कैसे सुनिश्चित करती है कि ढहने के मामले में, संक्रमण जल्दी समाप्त नहीं होता है, और विस्तार के मामले में, संक्रमण देर से शुरू नहीं होता है। सही बार से पता चलता है कि कैसे दोनो के संयोजन से बॉक्स सही समय पर पूरी ऊंचाई से शून्य ऊंचाई तक संक्रमण का कारण बनता है।
अपने डेमो के लिए मैं ऊपरी न्यूनतम मूल्य के रूप में 50px पर बस गया हूं। यह दूसरा मैजिक नंबर है, और यह बॉक्स की ऊंचाई से कम होना चाहिए। 50px उचित भी लगता है; ऐसा लगता नहीं है कि आप अक्सर एक ऐसा तत्व बनाना चाहते हैं जो पहले से 50 पिक्सेल ऊंचा न हो।
जैसा कि आप एनीमेशन में देख सकते हैं, परिणामस्वरूप परिवर्तन निरंतर है, लेकिन यह अलग नहीं है - उस समय जब न्यूनतम ऊंचाई पूरी मार्जिन के नीचे समायोजित मार्जिन के बराबर होती है, गति में अचानक बदलाव होता है। यह एनीमेशन में बहुत ध्यान देने योग्य है क्योंकि यह दोनों बदलावों के लिए एक रेखीय समय समारोह का उपयोग करता है, और क्योंकि संपूर्ण संक्रमण बहुत धीमा है। वास्तविक मामले में (शीर्ष पर मेरा डेमो), संक्रमण केवल 300ms लेता है, और निचला मार्जिन संक्रमण रैखिक नहीं है। मैंने दोनों संक्रमणों के लिए बहुत सारे अलग-अलग समय के कार्यों के साथ खेला है, और जिन लोगों ने मुझे महसूस किया कि वे विभिन्न प्रकार के मामलों के लिए सबसे अच्छा काम करते हैं।
दो समस्याओं को दूर करने के लिए:
- ऊपर से बिंदु, जहां 2000 से अधिक पिक्सेल ऊंचाई के बक्से पूरी तरह से ढह स्थिति में छिपे नहीं हैं,
- और रिवर्स समस्या, जहां गैर-छिपे मामले में, 50 पिक्सेल से कम ऊंचाई के बक्से संक्रमण के न होने पर भी बहुत अधिक होते हैं, क्योंकि न्यूनतम ऊंचाई उन्हें 50 पिक्सेल पर रखती है।
हम कंटेनर समस्या को एक max-height: 0
टूटे हुए मामले में एक 0s 0.3s
संक्रमण के साथ देकर पहली समस्या को हल करते हैं । इसका मतलब यह है कि यह वास्तव में एक संक्रमण नहीं है, लेकिन इसे max-height
देरी से लागू किया जाता है; संक्रमण समाप्त होने के बाद ही यह लागू होता है। इसके लिए सही तरीके से काम करने के लिए, हमें max-height
विपरीत, गैर-ढहने वाले, राज्य के लिए एक संख्यात्मक चुनने की भी आवश्यकता है । लेकिन 2000px मामले के विपरीत, जहां बहुत बड़ी संख्या में संक्रमण की गुणवत्ता को प्रभावित करता है, इस मामले में, यह वास्तव में कोई फर्क नहीं पड़ता। इसलिए हम केवल एक संख्या चुन सकते हैं जो इतनी अधिक हो कि हम जान सकें कि कोई भी ऊंचाई कभी भी इसके करीब नहीं आएगी। मैंने एक लाख पिक्सेल उठाए। यदि आपको लगता है कि आपको एक मिलियन पिक्सेल से अधिक की ऊंचाई की सामग्री का समर्थन करने की आवश्यकता हो सकती है, तो 1) मुझे क्षमा करें, और 2) बस एक जोड़ी शून्य जोड़ें।
दूसरी समस्या यही कारण है कि हम वास्तव min-height
में न्यूनतम ऊंचाई संक्रमण के लिए उपयोग नहीं कर रहे हैं । इसके बजाय, ::after
कंटेनर में एक छद्म तत्व है जिसमें height
50px से शून्य तक संक्रमण होता है। इसका एक ही प्रभाव होता है min-height
: यह वर्तमान में जो भी छद्म तत्व है उसे ऊंचाई से नीचे कंटेनर को सिकुड़ने नहीं देगा। लेकिन क्योंकि हम उपयोग कर रहे हैं height
, नहीं min-height
, हम अब max-height
संक्रमण के समाप्त होने के बाद छद्म तत्व की वास्तविक ऊंचाई को शून्य पर सेट करने के लिए (एक बार फिर देरी के साथ लागू) का उपयोग कर सकते हैं , यह सुनिश्चित करते हुए कि संक्रमण के बाहर कम से कम, यहां तक कि छोटे तत्व भी हैं सही ऊंचाई। क्योंकि min-height
है मजबूत की तुलना में max-height
, इस अगर हम इस्तेमाल किया कार्य नहीं करेगा कंटेनर है min-height
बजाय छद्म तत्व केheight
। वैसे ही जैसे max-height
पिछले पैराग्राफ में, इस max-height
भी संक्रमण के विपरीत छोर के लिए एक मूल्य की जरूरत है। लेकिन इस मामले में हम सिर्फ 50px ले सकते हैं।
Chrome (Win, Mac, Android, iOS), Firefox (Win, Mac, Android), Edge, IE11 (मेरे डेमो के साथ फ्लेक्सबॉक्स लेआउट समस्या को छोड़कर जो मैंने डिबगिंग को परेशान नहीं किया) और सफारी (Mac, iOS) में परीक्षण किया गया )। फ्लेक्सबॉक्स की बात करें तो, यह काम किसी भी फ्लेक्सबॉक्स का उपयोग किए बिना करना संभव होना चाहिए; वास्तव में मुझे लगता है कि आप IE7 में लगभग सब कुछ काम कर सकते हैं - इस तथ्य को छोड़कर कि आपके पास सीएसएस बदलाव नहीं होंगे, जिससे यह एक बेकार व्यायाम हो जाएगा।
height:auto/max-height
समाधान केवल तभी काम करेगा जब आप उस क्षेत्र का विस्तार कर रहे हों जोheight
आप प्रतिबंधित करना चाहते हैं। यदि आपके पास एकmax-height
है300px
, लेकिन एक कॉम्बो बॉक्स ड्रॉपडाउन, जो वापस आ सकता है50px
, तोmax-height
आपकी मदद नहीं करेगा,50px
तत्वों की संख्या के आधार पर परिवर्तनशील है, आप एक असंभव स्थिति में आ सकते हैं जहां मैं इसे ठीक नहीं कर सकता क्योंकि यहheight
नहीं है तय कियाheight:auto
गया, समाधान था, लेकिन मैं इसके साथ बदलाव का उपयोग नहीं कर सकता।