Skip to content

Instantly share code, notes, and snippets.

@n2o
Last active December 3, 2024 10:12
Show Gist options
  • Save n2o/322360e028e6c065fb4e1f7ea0a2ff65 to your computer and use it in GitHub Desktop.
Save n2o/322360e028e6c065fb4e1f7ea0a2ff65 to your computer and use it in GitHub Desktop.
Wordpress Shortcode: Get posts by category and sort them in a grid or slider

Let's assume we have a post type employee and some categories. We want to get all employees, who are assigned to a specific category to add them via a shortcode. The shortcode should have two attributes: category and layout. The category attribute should accept a comma-separated list of category slugs or IDs. The layout attribute should define the output layout: grid or slider.

Here is an example of how to create a shortcode to list employees by category:

/**
 * Listing of employees by category
 *
 * Example: [employee category="Physiotherapie" layout="grid"]
 * - layout="grid": Displays the employees in a grid (default)
 * - layout="slider": Displays the employees in a rotating gallery (slider)
 */
function employee_by_category($atts) {
    // Attributes with default values
    $atts = shortcode_atts(array(
        'category' => '', // Slugs or IDs of categories, separated by commas
        'layout'   => 'grid', // Layout type: grid (default) or slider
    ), $atts);

    // Extract category IDs
    $categories = array_map('trim', explode(',', $atts['category']));

    // Prepare query arguments
    $args = array(
        'post_type'     => 'employee', // Custom Post Type
        'post_status'   => 'publish',
        'orderby'       => 'rand',
        'order'         => 'ASC',
        'posts_per_page'=> -1,
    );

    // Add taxonomy only if categories are specified
    if (!empty($atts['category'])) {
        $args['tax_query'] = array(
            array(
                'taxonomy' => 'category', // Taxonomy of categories
                'field'    => 'slug', // Categories as slug (can be changed to 'term_id')
                'terms'    => $categories,
            ),
        );
    }

    $output = ''; // Output string
    $query = new WP_Query($args);

    // No posts found
    if (!$query->have_posts()) {
        return '<p>No entries found.</p>';
    }

    // Wrapper based on layout
    $class = ($atts['layout'] === 'slider') ? 'employee-slider' : 'employee-grid';

    $output .= '<div class="' . esc_attr($class) . '">';

    // Loop through posts
    while ($query->have_posts()) {
        $query->the_post();

        // Generate post HTML
        $output .= '<div class="employee-wrapper">';
        $output .= '<div class="employee-image">';
        $output .= '<a href="' . esc_url(get_permalink()) . '">';

        // Fallback for images
        if (has_post_thumbnail()) {
            $output .= get_the_post_thumbnail(get_the_ID(), 'medium'); // Thumbnail
        } else {
            $output .= '<img src="' . esc_url(get_template_directory_uri() . '/images/placeholder.png') . '" alt="' . esc_attr(get_the_title()) . '">';
        }

        $output .= '</a>';
        $output .= '</div>';
        if ($atts['layout'] === 'grid') {
            $output .= '<div class="employee-name">';
            $output .= '<h3>' . esc_html(get_the_title()) . '</h3>'; // Title
            $output .= '</div>';
        }
        $output .= '</div>';
    }

    $output .= '</div>'; // Close wrapper

    wp_reset_postdata(); // Reset query

    return $output;
}
add_shortcode('employee', 'employee_by_category');

Add this CSS to your Customizer or theme stylesheet to style the output:

/* Grid-Layout */
.employee-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px; /* Abstand zwischen den Karten */
  margin: 0 auto; /* Zentriert die Grid-Komponente */
  padding: 20px;
}

@media screen and (max-width: 1024px) {
  .employee-grid {
    grid-template-columns: repeat(2, 1fr); /* 2 Spalten auf Tablets */
  }
}

@media screen and (max-width: 768px) {
  .employee-grid {
    grid-template-columns: 1fr; /* 1 Spalte auf Mobile */
  }
}

/* Slider-Layout */
.employee-slider {
  display: flex;
  overflow: hidden; /* Versteckt den Überlauf */
  gap: 20px;
  padding: 40px;
  scroll-behavior: smooth; /* Sanftes Scrollen */
  scroll-snap-type: x mandatory; /* Erzwingt exakte Positionierung */

  position: relative; /* Für zukünftige Steuerung */
}

.employee-slider .employee-wrapper {
  flex: 0 0 auto;
  transition: transform 0.5s ease-in-out;
  scroll-snap-align: start; /* Jedes Element wird genau positioniert */
}

/* Animation für Rotation */
@keyframes slide {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-100%);
  }
}

.employee-slider:hover .employee-wrapper {
  animation-play-state: paused; /* Stoppe Rotation beim Hover */
}

/* Globale Styles */
.employee-wrapper {
  border: 0px solid #ddd; /* Leichter Rahmen */
  border-radius: 2px; /* Abgerundete Ecken */
  overflow: hidden; /* Überlauf verstecken */
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* Schatten */
  text-align: center; /* Zentrierter Inhalt */
  background: #fff; /* Hintergrundfarbe */
  transition: transform 0.3s ease, box-shadow 0.3s ease; /* Animation bei Hover */
}

.employee-wrapper:hover {
  transform: translateY(-5px); /* Leichter Hover-Effekt */
  box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15); /* Stärkere Schatten bei Hover */
}

.employee-image img {
  width: 100%; /* Bild füllt den Container */
  height: auto; /* Proportionale Höhe */
  display: block;
}

.employee-name {
  padding: 10px; /* Innenabstand */
  font-size: 1.2rem; /* Textgröße */
  font-weight: bold;
  color: #333; /* Textfarbe */
}

If you want to add the slider functionality, you need to add some JavaScript to let it automatically slide and pause on hover:

document.addEventListener("DOMContentLoaded", () => {
  const slider = document.querySelector(".employee-slider");
  if (!slider) return;

  const items = slider.querySelectorAll(".employee-wrapper");
  if (items.length === 0) return;

  // Calculate the exact width of an item including margin
  const itemStyle = getComputedStyle(items[0]);
  const itemWidth =
    items[0].offsetWidth +
    parseFloat(itemStyle.marginLeft) +
    parseFloat(itemStyle.marginRight);

  let currentIndex = 0;
  let interval;

  const startSlider = () => {
    interval = setInterval(() => {
      currentIndex++;

      // Return to the beginning if the end is reached
      if (currentIndex >= items.length) {
        currentIndex = 0;
        slider.scrollTo({
          left: 0,
          behavior: "smooth",
        });
      } else {
        slider.scrollTo({
          left: currentIndex * itemWidth,
          behavior: "smooth",
        });
      }
    }, 5000); // Scroll every 5 seconds
  };

  const stopSlider = () => clearInterval(interval);

  // Start the slider
  startSlider();

  // Stop the slider on hover
  slider.addEventListener("mouseover", stopSlider);

  // Restart the slider when the mouse leaves the area
  slider.addEventListener("mouseout", startSlider);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment