Flex Panels in CSS und JS
Ich habe die 30-Tage-Herausforderung von Wes Bos JS verfolgt, daher sind HTML und CSS meistens Copy-Paste. Ich hätte gerne Feedback zu JS (meistens). Vielen Dank.
const panels = document.querySelectorAll('.panel');
panels.forEach(panel => panel.addEventListener('click', () => {
const isOpen = panel.classList.contains('open');
panels.forEach(panel => panel.classList.remove('open'));
if(!isOpen) {
panel.classList.add('open');
}
}));
panels.forEach(panel => panel.addEventListener('transitionend', e => {
if(e.propertyName.includes('flex')) {
panels.forEach(panel => {
if(panel.classList.contains('open')) {
panel.classList.add('open-active');
} else {
panel.classList.remove('open-active');
}
});
}
}));
html {
box-sizing: border-box;
background: #ffc600;
font-family: 'helvetica neue';
font-size: 20px;
font-weight: 200;
}
body {
margin: 0;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
.panels {
min-height: 100vh;
overflow: hidden;
display: flex;
}
.panel {
background: #6B0F9C;
box-shadow: inset 0 0 0 5px rgba(255, 255, 255, 0.1);
color: white;
text-align: center;
align-items: center;
/* Safari transitionend event.propertyName === flex */
/* Chrome + FF transitionend event.propertyName === flex-grow */
transition:
font-size 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11),
flex 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11), background 0.2s;
font-size: 20px;
background-size: cover;
background-position: center;
flex: 1;
display: flex;
justify-content: center;
flex-direction: column;
}
.panel1 {
background-image: url(https://source.unsplash.com/gYl-UtwNg_I/1500x1500);
}
.panel2 {
background-image: url(https://source.unsplash.com/rFKUFzjPYiQ/1500x1500);
}
.panel3 {
background-image: url(https://images.unsplash.com/photo-1465188162913-8fb5709d6d57?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&w=1500&h=1500&fit=crop&s=967e8a713a4e395260793fc8c802901d);
}
.panel4 {
background-image: url(https://source.unsplash.com/ITjiVXcwVng/1500x1500);
}
.panel5 {
background-image: url(https://source.unsplash.com/3MNzGlQM7qs/1500x1500);
}
/* Flex Children */
.panel>* {
margin: 0;
width: 100%;
transition: transform 0.5s;
flex: 1 0 auto;
display: flex;
align-items: center;
justify-content: center;
}
.panel *:first-child {
transform: translateY(-100%);
}
.panel *:last-child {
transform: translateY(100%);
}
.panel.open-active *:first-child,
.panel.open-active *:last-child {
transform: translateY(0);
}
.panel p {
text-transform: uppercase;
font-family: 'Amatic SC', cursive;
text-shadow: 0 0 4px rgba(0, 0, 0, 0.72), 0 0 14px rgba(0, 0, 0, 0.45);
font-size: 2em;
}
.panel p:nth-child(2) {
font-size: 4em;
}
.panel.open {
font-size: 40px;
flex: 5;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flex Panels 💪</title>
<link href="https://fonts.googleapis.com/css?family=Amatic+SC" rel="stylesheet" type="text/css">
</head>
<body>
<div class="panels">
<div class="panel panel1">
<p>Hey</p>
<p>Let's</p>
<p>Dance</p>
</div>
<div class="panel panel2">
<p>Give</p>
<p>Take</p>
<p>Receive</p>
</div>
<div class="panel panel3">
<p>Experience</p>
<p>It</p>
<p>Today</p>
</div>
<div class="panel panel4">
<p>Give</p>
<p>All</p>
<p>You can</p>
</div>
<div class="panel panel5">
<p>Life</p>
<p>In</p>
<p>Motion</p>
</div>
</div>
</body>
</html>
Antworten
Es sieht ziemlich gut für mich aus. Ich kann nur ein paar Dinge sehen, die zu beachten sind:
Genauere Überprüfung des Eigenschaftsnamens, die Sie haben, if(e.propertyName.includes('flex')) {weil Safari flexund andere verwenden flex-grow. Sind Sie sicher, dass der flexTeilstring in keinem anderen möglichen CSS-Übergang vorhanden ist? Selbst wenn Sie sicher sind, werden die Leser des Codes sicher sein? Ich würde zu einem ===Test gegen beide Möglichkeiten wechseln oder zumindest verwenden startsWith(was etwas angemessener ist als .includeshier, da beide Möglichkeiten mit beginnen flex).
Sie können den Kommentar zum Namen des Übergangsereignisses auch in das JS sowie in das CSS verschieben.
Prägnante Einstellung für classList Wenn Sie basierend auf einer Bedingung entweder einen Klassennamen hinzufügen oder einen Klassennamen entfernen möchten, können Sie if(...) classList.add(...) else(...) classList.removeeinen classList.togglemit einem zweiten Argument zu einem einzigen zusammenfassen , das angibt, ob die Klasse hinzugefügt oder entfernt werden soll. Ihre
if(panel.classList.contains('open')) {
panel.classList.add('open-active');
} else {
panel.classList.remove('open-active');
}
vereinfacht zu
const { classList } = panel;
classList.toggle('open-active', classList.contains('open'));
Browserkompatibilität Obwohl einige alte Browser das zweite Argument nicht unterstützen, sollten Sie überlegen, welche Art von Browsern Sie unterstützen müssen. Wenn Sie nur einigermaßen aktuelle Browser unterstützen möchten, ist dies in Ordnung. Eine andere Sache, die Sie beachten sollten, ist, dass sie NodeList.prototype.forEacherst vor einigen Jahren eingeführt wurde, etwa 2016 oder 2017 IIRC; wie startsWith, es ist neuer als ES6, also entweder eine polyfill oder Verwendung Iteratoren und Babel verwendet statt, zum Beispiel:
for (const panel of panels) {
// do stuff with panel
(Wenn Sie IE unterstützen möchten, sollten Sie trotzdem Babel verwenden, um Ihren Code in die ES5-Syntax zu transpilieren.)
Leere Rückkehr? panels.forEach(panel => panel.addEventListenerGibt den Wert des Aufrufs addEventListeneran den Aufrufer von zurück forEach. Da forEachnicht geprüft wird, was die Rückrufe zurückgeben, wird hierdurch nichts unternommen. Es ist kein wirkliches Problem, aber einige halten den Code möglicherweise für etwas sinnvoller, wenn der forEachRückruf ungültig zurückgegeben wird (überhaupt keine returnAnweisung oder implizite Rückgabe). (In TSLint von TypeScript hier beschrieben )
Klickbare Bedienfelder Da die Bedienfelder anklickbar sind, können Sie möglicherweise vom Standardcursor zu wechseln cursor: pointer, um dem Benutzer klarer zu machen, dass sie angeklickt werden sollen.
Raum zwischen den Elementen in Selektoren ich ändern würde , .panel>*um .panel > *- es macht es ein wenig einfacher zu lesen , wenn einzelne Elemente durch Leerzeichen voneinander getrennt sind.
Repetitive Panels anstatt
<div class="panel panel1">
</div>
<div class="panel panel2">
</div>
.panel1 {
background-image: url(https://source.unsplash.com/gYl-UtwNg_I/1500x1500);
}
.panel2 {
background-image: url(https://source.unsplash.com/rFKUFzjPYiQ/1500x1500);
}
Verwenden Sie :nth-childstattdessen, damit Sie die zusätzlichen panel#Klassen vollständig entfernen können .
.panel:nth-child(1) {
background-image: url(https://source.unsplash.com/gYl-UtwNg_I/1500x1500);
}
.panel:nth-child(2) {
background-image: url(https://source.unsplash.com/rFKUFzjPYiQ/1500x1500);
}
const panels = document.querySelectorAll('.panel');
panels.forEach((panel) => {
panel.addEventListener('click', () => {
const isOpen = panel.classList.contains('open');
panels.forEach(panel => panel.classList.remove('open'));
if (!isOpen) {
panel.classList.add('open');
}
});
});
panels.forEach((panel) => {
panel.addEventListener('transitionend', e => {
/* Safari transitionend event.propertyName === flex */
/* Chrome + FF transitionend event.propertyName === flex-grow */
if (e.propertyName === 'flex' || e.propertyName === 'flex-grow') {
panels.forEach(panel => {
const { classList } = panel;
classList.toggle('open-active', classList.contains('open'));
});
}
})
});
html {
box-sizing: border-box;
background: #ffc600;
font-family: 'helvetica neue';
font-size: 20px;
font-weight: 200;
}
body {
margin: 0;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
.panels {
min-height: 100vh;
overflow: hidden;
display: flex;
}
.panel {
background: #6B0F9C;
box-shadow: inset 0 0 0 5px rgba(255, 255, 255, 0.1);
color: white;
text-align: center;
align-items: center;
/* Safari transitionend event.propertyName === flex */
/* Chrome + FF transitionend event.propertyName === flex-grow */
transition:
font-size 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11),
flex 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11), background 0.2s;
font-size: 20px;
background-size: cover;
background-position: center;
flex: 1;
display: flex;
justify-content: center;
flex-direction: column;
cursor: pointer;
}
.panel:nth-child(1) {
background-image: url(https://source.unsplash.com/gYl-UtwNg_I/1500x1500);
}
.panel:nth-child(2) {
background-image: url(https://source.unsplash.com/rFKUFzjPYiQ/1500x1500);
}
.panel:nth-child(3) {
background-image: url(https://images.unsplash.com/photo-1465188162913-8fb5709d6d57?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&w=1500&h=1500&fit=crop&s=967e8a713a4e395260793fc8c802901d);
}
.panel:nth-child(4) {
background-image: url(https://source.unsplash.com/ITjiVXcwVng/1500x1500);
}
.panel:nth-child(5) {
background-image: url(https://source.unsplash.com/3MNzGlQM7qs/1500x1500);
}
/* Flex Children */
.panel > * {
margin: 0;
width: 100%;
transition: transform 0.5s;
flex: 1 0 auto;
display: flex;
align-items: center;
justify-content: center;
}
.panel *:first-child {
transform: translateY(-100%);
}
.panel *:last-child {
transform: translateY(100%);
}
.panel.open-active *:first-child,
.panel.open-active *:last-child {
transform: translateY(0);
}
.panel p {
text-transform: uppercase;
font-family: 'Amatic SC', cursive;
text-shadow: 0 0 4px rgba(0, 0, 0, 0.72), 0 0 14px rgba(0, 0, 0, 0.45);
font-size: 2em;
}
.panel p:nth-child(2) {
font-size: 4em;
}
.panel.open {
font-size: 40px;
flex: 5;
}
<link href="https://fonts.googleapis.com/css?family=Amatic+SC" rel="stylesheet" type="text/css">
<div class="panels">
<div class="panel">
<p>Hey</p>
<p>Let's</p>
<p>Dance</p>
</div>
<div class="panel">
<p>Give</p>
<p>Take</p>
<p>Receive</p>
</div>
<div class="panel">
<p>Experience</p>
<p>It</p>
<p>Today</p>
</div>
<div class="panel">
<p>Give</p>
<p>All</p>
<p>You can</p>
</div>
<div class="panel">
<p>Life</p>
<p>In</p>
<p>Motion</p>
</div>
</div>
Rezension
Ich stimme der Antwort von CertainPerformance zu: Der Code sieht ziemlich gut aus. Der Einzug scheint konsistent zu sein, Variablennamen sind angemessen und Zeilen sind gut terminiert. Die Lesbarkeit ist großartig.
Ineffiziente Schleifen
Der Code in den Ereignishandlern durchläuft alle Bedienfeldelemente, jedoch werden höchstens zwei Elemente in ihrer Klassenliste aktualisiert. Im folgenden Vorschlag finden Sie Informationen zum Entfernen der Schleifen.
Überschüssige CSS-Regeln
Der Stil font-size: 20px;wird unter nicht benötigt, .panelda dieselbe Regel auch für angegeben htmlist und von spezifischeren Selektoren überschrieben wird.
Vorschlag
Entfernen Sie Schleifen mithilfe der Ereignisdelegierung
Anstatt jedem Panel-Element Ereignis-Listener hinzuzufügen, können dem Container-Element Ereignis-Listener hinzugefügt werden. Dies würde erfordern, dass die Ereignishandler geändert werden, um das Ereignisziel anzuzeigen und festzustellen, ob das Ziel mit einem Panel oder untergeordneten Element eines Panels übereinstimmt. Dies kann mit der .closest()Methode durchgeführt werden. Eine Live- HTMLCollection von Elementen mit Klassennamen openkann einmal mit abgerufen werden document.getElementsByClassName('open');. Wenn dann Elemente beim Aufrufen des Klick-Handlers diese Klasse haben, kann der Klassenname entfernt werden.
Dies würde das Hinzufügen und Entfernen von Bedienfeldern ermöglichen, ohne dass die Ereignishandler darauf registriert werden müssen. Während es auf einer kleinen Seite wie dieser möglicherweise keinen merklichen Unterschied macht, ist es ratsam, Stellen zu berücksichtigen, an denen eine Schleife vermieden werden kann.
Im folgenden Codeausschnitt wurden die Schleifen entfernt.
const panelsContainer = document.querySelector('.panels');
const openPanels = document.getElementsByClassName('open');
const openActivePanels = document.getElementsByClassName('open-active');
panelsContainer.addEventListener('click', e => {
const panel = e.target.closest('.panel');
if (!panel) {
return;
}
const isOpen = panel.classList.contains('open');
if (openPanels.length) {
openPanels[0].classList.remove('open');
}
panel.classList.toggle('open', !isOpen);
});
panelsContainer.addEventListener('transitionend', e => {
/* Safari transitionend event.propertyName === flex */
/* Chrome + FF transitionend event.propertyName === flex-grow */
if (e.propertyName === 'flex' || e.propertyName === 'flex-grow') {
if (openActivePanels.length) {
openActivePanels[0].classList.toggle('open-active', openActivePanels[0].classList.contains('open'))
}
if (openPanels.length) {
openPanels[0].classList.add('open-active')
}
}
})
html {
box-sizing: border-box;
background: #ffc600;
font-family: 'helvetica neue';
font-size: 20px;
font-weight: 200;
}
body {
margin: 0;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
.panels {
min-height: 100vh;
overflow: hidden;
display: flex;
}
.panel {
background: #6B0F9C;
box-shadow: inset 0 0 0 5px rgba(255, 255, 255, 0.1);
color: white;
text-align: center;
align-items: center;
/* Safari transitionend event.propertyName === flex */
/* Chrome + FF transitionend event.propertyName === flex-grow */
transition: font-size 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11), flex 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11), background 0.2s;
background-size: cover;
background-position: center;
flex: 1;
display: flex;
justify-content: center;
flex-direction: column;
}
.panel1 {
background-image: url(https://source.unsplash.com/gYl-UtwNg_I/1500x1500);
}
.panel2 {
background-image: url(https://source.unsplash.com/rFKUFzjPYiQ/1500x1500);
}
.panel3 {
background-image: url(https://images.unsplash.com/photo-1465188162913-8fb5709d6d57?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&w=1500&h=1500&fit=crop&s=967e8a713a4e395260793fc8c802901d);
}
.panel4 {
background-image: url(https://source.unsplash.com/ITjiVXcwVng/1500x1500);
}
.panel5 {
background-image: url(https://source.unsplash.com/3MNzGlQM7qs/1500x1500);
}
/* Flex Children */
.panel>* {
margin: 0;
width: 100%;
transition: transform 0.5s;
flex: 1 0 auto;
display: flex;
align-items: center;
justify-content: center;
}
.panel *:first-child {
transform: translateY(-100%);
}
.panel *:last-child {
transform: translateY(100%);
}
.panel.open-active *:first-child,
.panel.open-active *:last-child {
transform: translateY(0);
}
.panel p {
text-transform: uppercase;
font-family: 'Amatic SC', cursive;
text-shadow: 0 0 4px rgba(0, 0, 0, 0.72), 0 0 14px rgba(0, 0, 0, 0.45);
font-size: 2em;
}
.panel p:nth-child(2) {
font-size: 4em;
}
.panel.open {
font-size: 40px;
flex: 5;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flex Panels 💪</title>
<link href="https://fonts.googleapis.com/css?family=Amatic+SC" rel="stylesheet" type="text/css">
</head>
<body>
<div class="panels">
<div class="panel panel1">
<p>Hey</p>
<p>Let's</p>
<p>Dance</p>
</div>
<div class="panel panel2">
<p>Give</p>
<p>Take</p>
<p>Receive</p>
</div>
<div class="panel panel3">
<p>Experience</p>
<p>It</p>
<p>Today</p>
</div>
<div class="panel panel4">
<p>Give</p>
<p>All</p>
<p>You can</p>
</div>
<div class="panel panel5">
<p>Life</p>
<p>In</p>
<p>Motion</p>
</div>
</div>
</body>
</html>