Lesson 16-Common JavaScript Effects and Features Examples

Waterfall Layout

<div id="container">
  <!-- Items will be dynamically added here -->
</div>

<!-- Example item template -->
<div class="item" style="display: none;">
  <img src="" alt="">
</div>
<style>
#container {
  column-count: 3; /* Adjust the number of columns as desired */
  column-gap: 1em;
}

.item {
  break-inside: avoid;
  margin-bottom: 1em;
}

.item img {
  width: 100%; /* Adjust image size as needed */
  height: auto;
}
</style>
<script>
function createWaterfall(container, items, itemTemplate) {
  const containerEl = document.getElementById(container);
  const itemTemplateEl = document.querySelector(itemTemplate);

  // Clone and append items to the container
  for (let i = 0; i < items.length; i++) {
    const itemClone = itemTemplateEl.cloneNode(true);
    const imgEl = itemClone.querySelector('img');
    imgEl.src = items[i].src; // Replace with your actual image source

    containerEl.appendChild(itemClone);
    itemClone.style.display = 'block'; // Show the cloned item
  }
}

// Example usage:
const items = [
  { src: 'image1.jpg' },
  { src: 'image2.jpg' },
  // ... more items
];

createWaterfall('container', items, '.item');
</script>
<div id="carousel">
  <div class="carousel-track">
    <div class="carousel-item active">
      <img src="image1.jpg" alt="Image 1">
    </div>
    <div class="carousel-item">
      <img src="image2.jpg" alt="Image 2">
    </div>
    <!-- Add more carousel items as needed -->
  </div>
  <button id="prevBtn" class="carousel-control">Previous</button>
  <button id="nextBtn" class="carousel-control">Next</button>
</div>
<style>
#carousel {
  position: relative;
  overflow: hidden;
  width: 100%;
  /* Set height or aspect ratio as needed */
}

.carousel-track {
  display: flex;
  position: relative;
  transition: transform 0.5s ease-in-out;
}

.carousel-item {
  flex-shrink: 0;
  width: 100%;
  /* Set height or aspect ratio as needed */
}

.carousel-item img {
  width: 100%;
  height: auto;
}

/* Hide non-active items by default */
.carousel-item:not(.active) {
  display: none;
}

.carousel-control {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  cursor: pointer;
}

#prevBtn {
  left: 10px;
}

#nextBtn {
  right: 10px;
}
</style>
document.addEventListener("DOMContentLoaded", function () {
  const carousel = document.getElementById("carousel");
  const track = carousel.querySelector(".carousel-track");
  const items = carousel.querySelectorAll(".carousel-item");
  const prevBtn = document.getElementById("prevBtn");
  const nextBtn = document.getElementById("nextBtn");

  let currentIndex = 0;

  function setActiveItem(index) {
    items[currentIndex].classList.remove("active");
    items[index].classList.add("active");
    track.style.transform = `translateX(${-index * 100}%)`;
    currentIndex = index;
  }

  function showPrev() {
    const prevIndex = (currentIndex - 1 + items.length) % items.length;
    setActiveItem(prevIndex);
  }

  function showNext() {
    const nextIndex = (currentIndex + 1) % items.length;
    setActiveItem(nextIndex);
  }

  prevBtn.addEventListener("click", showPrev);
  nextBtn.addEventListener("click", showNext);
});

Tab Switching

<div id="tabContainer">
  <ul class="tab-nav">
    <li class="active" data-tab="tab1">Tab 1</li>
    <li data-tab="tab2">Tab 2</li>
    <li data-tab="tab3">Tab 3</li>
  </ul>

  <div class="tab-content active" id="tab1">
    Content for Tab 1
  </div>
  <div class="tab-content" id="tab2">
    Content for Tab 2
  </div>
  <div class="tab-content" id="tab3">
    Content for Tab 3
  </div>
</div>
<style>
.tab-nav {
  list-style-type: none;
  padding: 0;
  margin: 0;
}

.tab-nav li {
  display: inline-block;
  padding: 10px;
  cursor: pointer;
}

.tab-nav li.active {
  font-weight: bold;
  /* Add any additional active styles you want */
}

.tab-content {
  display: none;
}

.tab-content.active {
  display: block;
}
</style>
document.addEventListener("DOMContentLoaded", function () {
  const tabNavItems = document.querySelectorAll("#tabContainer .tab-nav li");
  const tabContents = document.querySelectorAll("#tabContainer .tab-content");

  function activateTab(tabName) {
    tabNavItems.forEach(function (navItem) {
      navItem.classList.remove("active");
      if (navItem.dataset.tab === tabName) {
        navItem.classList.add("active");
      }
    });

    tabContents.forEach(function (content) {
      content.classList.remove("active");
      if (content.id === tabName) {
        content.classList.add("active");
      }
    });
  }

  tabNavItems.forEach(function (navItem) {
    navItem.addEventListener("click", function () {
      const targetTab = navItem.dataset.tab;
      activateTab(targetTab);
    });
  });
});

Accordion

<div class="accordion">
  <div class="accordion-item">
    <h3 class="accordion-header" data-toggle="accordion">
      Header 1
    </h3>
    <div class="accordion-content" data-content="accordion">
      Content 1
    </div>
  </div>
  <div class="accordion-item">
    <h3 class="accordion-header" data-toggle="accordion">
      Header 2
    </h3>
    <div class="accordion-content" data-content="accordion">
      Content 2
    </div>
  </div>
  <!-- Add more accordion items as needed -->
</div>
<style>
.accordion {
  border: 1px solid #ccc;
}

.accordion-item {
  border-bottom: 1px solid #ccc;
}

.accordion-header {
  cursor: pointer;
  padding: 10px;
  background-color: #f8f8f8;
}

.accordion-content {
  display: none;
  padding: 10px;
}

.accordion-content.active {
  display: block;
}
</style>
document.addEventListener("DOMContentLoaded", function () {
  const headers = document.querySelectorAll(".accordion-header");
  const contents = document.querySelectorAll(".accordion-content");

  function toggleAccordion(header, content) {
    header.classList.toggle("active");
    content.classList.toggle("active");
  }

  headers.forEach(function (header, index) {
    header.addEventListener("click", function () {
      // Close all other open content areas
      contents.forEach(function (content) {
        content.classList.remove("active");
      });

      // Open the clicked content area
      const targetContent = contents[index];
      toggleAccordion(header, targetContent);
    });
  });
});

Window Dragging

<div id="draggableWindow" class="window">
  <!-- Window content -->
</div>
<style>
.window {
  position: absolute;
  width: 300px;
  height: 200px;
  border: 1px solid #ccc;
  padding: 10px;
  cursor: move;
}
</style>
document.addEventListener("DOMContentLoaded", function () {
  const draggableWindow = document.getElementById("draggableWindow");

  let isDragging = false;
  let dragStartX, dragStartY;

  draggableWindow.addEventListener("mousedown", function (event) {
    isDragging = true;
    dragStartX = event.clientX - draggableWindow.offsetLeft;
    dragStartY = event.clientY - draggableWindow.offsetTop;
  });

  document.addEventListener("mousemove", function (event) {
    if (isDragging) {
      draggableWindow.style.left = `${event.clientX - dragStartX}px`;
      draggableWindow.style.top = `${event.clientY - dragStartY}px`;
    }
  });

  document.addEventListener("mouseup", function () {
    isDragging = false;
  });
});

Custom Context Menu

<div id="contextMenuTarget">
  Right-click on this element to see the custom context menu.
</div>

<div id="customContextMenu" class="hidden">
  <ul>
    <li onclick="handleContextAction('action1')">Action 1</li>
    <li onclick="handleContextAction('action2')">Action 2</li>
    <!-- Add more actions as needed -->
  </ul>
</div>
<style>
.hidden {
  display: none;
}

#customContextMenu {
  position: absolute;
  background-color: #fff;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
  list-style-type: none;
  padding: 0;
  margin: 0;
}

#customContextMenu li {
  padding: 10px;
  cursor: pointer;
}

#customContextMenu li:hover {
  background-color: #f0f0f0;
}
</style>
document.addEventListener("DOMContentLoaded", function () {
  const contextMenuTarget = document.getElementById("contextMenuTarget");
  const customContextMenu = document.getElementById("customContextMenu");

  let contextMenuOpen = false;

  function openContextMenu(event) {
    event.preventDefault();
    const mousePos = { x: event.clientX, y: event.clientY };
    customContextMenu.style.left = `${mousePos.x}px`;
    customContextMenu.style.top = `${mousePos.y}px`;
    customContextMenu.classList.remove("hidden");
    contextMenuOpen = true;
  }

  function closeContextMenu() {
    customContextMenu.classList.add("hidden");
    contextMenuOpen = false;
  }

  function handleContextAction(action) {
    console.log(`Performing action: ${action}`);
    closeContextMenu();
  }

  contextMenuTarget.addEventListener("contextmenu", openContextMenu);

  document.addEventListener("click", function (event) {
    if (!contextMenuTarget.contains(event.target) && contextMenuOpen) {
      closeContextMenu();
    }
  });

  document.addEventListener("keydown", function (event) {
    if (event.key === "Escape" && contextMenuOpen) {
      closeContextMenu();
    }
  });
});

Multi-Level Tabs

Implements a hierarchical navigation menu:

<ul class="tabs">
  <li class="active">Tab 1
    <ul class="sub-tabs">
      <li class="active">Sub Tab 1.1</li>
      <li>Sub Tab 1.2</li>
    </ul>
  </li>
  <li>Tab 2</li>
</ul>

<script>
const tabs = document.querySelectorAll('.tabs li');
tabs.forEach((tab) => {
  tab.addEventListener('click', () => {
    tab.classList.add('active');
    tabs.forEach((otherTab) => {
      if (otherTab !== tab) otherTab.classList.remove('active');
    });
  });
});

const subTabs = document.querySelectorAll('.sub-tabs li');
subTabs.forEach((subTab) => {
  subTab.addEventListener('click', () => {
    subTab.classList.add('active');
    subTabs.forEach((otherSubTab) => {
      if (otherSubTab !== subTab) otherSubTab.classList.remove('active');
    });
  });
});
</script>

Share your love