Search And Filtering Of Sessions On Event Registration Form

I love getting questions from people that are using Customer Insights – Journeys. I have a lot of ideas but there are scenarios that I just never come up against myself. One was a question that asked if it might be possible to add a search box or filtering to an event registration form that has sessions. In that readers case, they had about 100 sessions that needed to be displayed. What a pain for the person trying to register, having to scroll through and read all of them just to find the right ones to pick. I accepted the challenge and built some options with search and filtering of sessions that works nicely with a keyword search, category drop down, date selector, time option, ability to reset all the filters, and a counter that shows how many sessions are found each time you filter along with the number of sessions registered for. The cherry on the cake is the ability to block someone from registering for another session on the exact same date and time. Let’s check it out!

The first thing I would suggest is consider the layout of the sessions. By default they will be a long list like this.

Click to view in detail

Simply add this in to your HTML in the <style> section near the top. Notice the grid-template-columns part? The repeat of 2 will show us two sessions per row. Make sure you also include the media only screen part which determines how it will look on a mobile. You should leave this as repeat 1 so that each session shows on a single row stacked on top of each other. I also reduced the gap between each one from 20px to 4px.

    //Event Sessions   
    div[data-editorblocktype="Sessions"] .eventSessions {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 20px;
}
.eventSessions {
    font-size: 16px;
    line-height: 1.2;
    border: none;
    display: grid !important;
    grid-template-columns: repeat(2, 1fr);
    gap: 20px;
}

 @media only screen and (max-width: 768px) {
  div[data-editorblocktype="Sessions"] .eventSessions {
  grid-template-columns: repeat(1, 1fr); /* one column */
gap: 4px;
 }
 .eventSessions {
    grid-template-columns: repeat(1, 1fr);/* one column */
gap: 4px;
}
 }

This works great for my mine because I have two sessions on each date at the same time. Makes it nice and easy to review.

Click to view in detail

You could change that 2 to a 3 and get even more per row. You get the idea.

Click to view in detail

Let’s start of small on the form and work our way up to the final big all singing all dancing finished product. First, we could add in a keyword search box at the top. This will allow someone to type something that will filter the sessions based on the session title and/or session description. I also added in a counter that shows how many sessions have been found that match the keyword search.

Click to view in detail

If this is all you would want, great, you can copy the script below.

<script>
document.addEventListener("d365mkt-afterformload", function () {
  const sessionsDiv = document.querySelector("div[data-editorblocktype="Sessions"]");
  if (!sessionsDiv) return;

  // --- Create keyword search box ---
  const keywordBox = document.createElement("input");
  keywordBox.type = "text";
  keywordBox.placeholder = "Search sessions...";
  keywordBox.style.width = "100%";
  keywordBox.style.padding = "8px";
  keywordBox.style.margin = "10px 0 5px 0";
  keywordBox.style.boxSizing = "border-box";

  // --- Create results counter ---
  const resultsCounter = document.createElement("div");
  resultsCounter.style.margin = "0 0 10px 0";
  resultsCounter.style.fontStyle = "italic";
  resultsCounter.style.fontSize = "14px";
  resultsCounter.textContent = "";

  // Insert counter and search box above sessions
  sessionsDiv.insertAdjacentElement("beforebegin", resultsCounter);
  sessionsDiv.insertAdjacentElement("beforebegin", keywordBox);

  function filterSessions() {
    const term = keywordBox.value.toLowerCase();
    const sessionDivs = document.querySelectorAll(".eventSession");
    let visibleCount = 0;

    sessionDivs.forEach(session => {
      const descDiv = session.querySelector(".eventSessionDescription");
      if (!descDiv) return;

      // Combine all visible personalization spans in the session for search
      let textToSearch = "";
      descDiv.querySelectorAll("span.msdynmkt_personalization").forEach(span => {
        if (span.offsetParent !== null) { // only visible
          textToSearch += " " + span.textContent.toLowerCase();
        }
      });

      // Show/hide the session using !important to override CSS
      if (term === "" || textToSearch.includes(term)) {
        session.style.setProperty("display", "flex", "important");
        visibleCount++;
      } else {
        session.style.setProperty("display", "none", "important");
      }
    });

    // Update counter text
    if (visibleCount === 0) {
      resultsCounter.textContent = "No sessions found.";
    } else if (visibleCount === 1) {
      resultsCounter.textContent = "1 session found.";
    } else {
      resultsCounter.textContent = `${visibleCount} sessions found.`;
    }
  }

  // --- Event listener ---
  keywordBox.addEventListener("input", filterSessions);

  // Initial run
  filterSessions();
});
</script>

I think we can do better though. What about a category? You might think this is impossible, but all we need to do is include the word Category: followed by whatever you want to use at the end of each Session Summary on the individual session record. The script can then extract the final part that comes after Category: and make a nice little drop down list for us. Beautiful! You could do something like that with Location or other filtering options you want to create.

Click to view in detail

If you want the option above, use the script below.

<script>
document.addEventListener("d365mkt-afterformload", function () {
  const sessionsDiv = document.querySelector("div[data-editorblocktype="Sessions"]");
  if (!sessionsDiv) return;

  const filterContainer = document.createElement("div");
  filterContainer.style.display = "flex";
  filterContainer.style.flexWrap = "wrap";
  filterContainer.style.gap = "10px";
  filterContainer.style.marginBottom = "10px";

  // --- Keyword search ---
  const keywordBox = document.createElement("input");
  keywordBox.type = "text";
  keywordBox.placeholder = "Search sessions...";
  keywordBox.style.flex = "1";
  keywordBox.style.minWidth = "150px";
  keywordBox.style.padding = "8px";

  // --- Category dropdown ---
  const categorySelect = document.createElement("select");
  categorySelect.style.flex = "1";
  categorySelect.style.minWidth = "150px";
  categorySelect.style.padding = "8px";
  categorySelect.innerHTML = `<option value="">Filter by category</option>`;

  // --- Results counter ---
  const resultsCounter = document.createElement("div");
  resultsCounter.style.margin = "0 0 10px 0";
  resultsCounter.style.fontStyle = "italic";
  resultsCounter.style.fontSize = "14px";
  resultsCounter.textContent = "";

  filterContainer.appendChild(keywordBox);
  filterContainer.appendChild(categorySelect);

  sessionsDiv.insertAdjacentElement("beforebegin", resultsCounter);
  sessionsDiv.insertAdjacentElement("beforebegin", filterContainer);

  const sessionDivs = document.querySelectorAll(".eventSession");
  const categories = new Set();

  // --- Collect unique categories ---
  sessionDivs.forEach(session => {
    const descDiv = session.querySelector(".eventSessionDescription");
    if (!descDiv) return;
    const text = descDiv.textContent;

    const catMatch = text.match(/Category:s*(.+)/i);
    if (catMatch) categories.add(catMatch[1].trim());
  });

  // Populate category dropdown
  Array.from(categories).sort().forEach(cat => 
    categorySelect.innerHTML += `<option value="${cat}">${cat}</option>`
  );

  // --- Filter function ---
  function filterSessions() {
    const term = keywordBox.value.toLowerCase();
    const selectedCategory = categorySelect.value.toLowerCase();
    let visibleCount = 0;

    sessionDivs.forEach(session => {
      const descDiv = session.querySelector(".eventSessionDescription");
      if (!descDiv) return;

      // Keyword search
      let textToSearch = "";
      descDiv.querySelectorAll("span.msdynmkt_personalization").forEach(span => {
        if (span.offsetParent !== null) textToSearch += " " + span.textContent.toLowerCase();
      });

      // Category
      const catMatch = descDiv.textContent.match(/Category:s*(.+)/i);
      const categoryText = catMatch ? catMatch[1].trim().toLowerCase() : "";

      const keywordMatch = term === "" || textToSearch.includes(term);
      const categoryMatch = selectedCategory === "" || categoryText === selectedCategory;

      if (keywordMatch && categoryMatch) {
        session.style.setProperty("display", "flex", "important");
        visibleCount++;
      } else {
        session.style.setProperty("display", "none", "important");
      }
    });

    resultsCounter.textContent = visibleCount === 0 ? "No sessions found." :
                                  visibleCount === 1 ? "1 session found." :
                                  `${visibleCount} sessions found.`;
  }

  [keywordBox, categorySelect].forEach(el => {
    el.addEventListener("input", filterSessions);
    el.addEventListener("change", filterSessions);
  });

  // Initial run
  filterSessions();
});
</script>

If your event spans across several days, it would be helpful to have a date and time picker right? This way someone can look for everything on a specific date and even time, or just filter by time and show everything across all days at that time. Lots of options

Click to view in detail

This one will also include the date and time filters. The calendar will only allow selection of dates when the event is actually held, and the calendar will default to start on the first date it finds in the list of all the sessions.

<script>
document.addEventListener("d365mkt-afterformload", function () {
  const sessionsDiv = document.querySelector("div[data-editorblocktype="Sessions"]");
  if (!sessionsDiv) return;

  const filterContainer = document.createElement("div");
  filterContainer.style.display = "flex";
  filterContainer.style.flexWrap = "wrap";
  filterContainer.style.gap = "10px";
  filterContainer.style.marginBottom = "10px";

  // --- Keyword search ---
  const keywordBox = document.createElement("input");
  keywordBox.type = "text";
  keywordBox.placeholder = "Search sessions...";
  keywordBox.style.flex = "1";
  keywordBox.style.minWidth = "150px";
  keywordBox.style.padding = "8px";

  // --- Category dropdown ---
  const categorySelect = document.createElement("select");
  categorySelect.style.flex = "1";
  categorySelect.style.minWidth = "150px";
  categorySelect.style.padding = "8px";
  categorySelect.innerHTML = `<option value="">Filter by category</option>`;

  // --- Date picker ---
  const dateInput = document.createElement("input");
  dateInput.type = "date";
  dateInput.style.flex = "1";
  dateInput.style.minWidth = "150px";
  dateInput.style.padding = "8px";

  // --- Time dropdown ---
  const timeSelect = document.createElement("select");
  timeSelect.style.flex = "1";
  timeSelect.style.minWidth = "150px";
  timeSelect.style.padding = "8px";
  timeSelect.innerHTML = `<option value="">Filter by time</option>`;

  // --- Results counter ---
  const resultsCounter = document.createElement("div");
  resultsCounter.style.margin = "0 0 10px 0";
  resultsCounter.style.fontStyle = "italic";
  resultsCounter.style.fontSize = "14px";
  resultsCounter.textContent = "";

  filterContainer.appendChild(keywordBox);
  filterContainer.appendChild(categorySelect);
  filterContainer.appendChild(dateInput);
  filterContainer.appendChild(timeSelect);

  sessionsDiv.insertAdjacentElement("beforebegin", resultsCounter);
  sessionsDiv.insertAdjacentElement("beforebegin", filterContainer);

  const sessionDivs = document.querySelectorAll(".eventSession");
  const categories = new Set();
  const times = new Set();
  const sessionDates = new Set();

  // --- Helper to convert session date to YYYY-MM-DD ---
  function sessionDateToISO(dateStr) {
    const parts = dateStr.split("/");
    return `${parts[2]}-${parts[1].padStart(2, "0")}-${parts[0].padStart(2, "0")}`;
  }

  // --- Helper to convert time to minutes for sorting ---
  function timeToNumber(timeStr) {
    const match = timeStr.match(/(d{1,2}):(d{2})s*([AP]M)/i);
    if (!match) return 0;
    let hours = parseInt(match[1], 10);
    const minutes = parseInt(match[2], 10);
    const ampm = match[3].toUpperCase();
    if (ampm === "PM" && hours < 12) hours += 12;
    if (ampm === "AM" && hours === 12) hours = 0;
    return hours * 60 + minutes;
  }

  // --- Collect unique categories, times, and dates ---
  sessionDivs.forEach(session => {
    const descDiv = session.querySelector(".eventSessionDescription");
    if (!descDiv) return;
    const text = descDiv.textContent;

    const catMatch = text.match(/Category:s*(.+)/i);
    if (catMatch) categories.add(catMatch[1].trim());

    const dateMatch = text.match(/d{1,2}/d{1,2}/d{4}/);
    if (dateMatch) sessionDates.add(sessionDateToISO(dateMatch[0]));

    const timeMatch = text.match(/bd{1,2}:d{2}s?[AP]Mb/i);
    if (timeMatch) times.add(timeMatch[0].trim());
  });

  // Populate category dropdown
  Array.from(categories).sort().forEach(cat => categorySelect.innerHTML += `<option value="${cat}">${cat}</option>`);

  // Populate time dropdown sorted chronologically
  Array.from(times).sort((a, b) => timeToNumber(a) - timeToNumber(b))
       .forEach(t => timeSelect.innerHTML += `<option value="${t}">${t}</option>`);

  // --- Set min/max and initial date for the calendar ---
  const sortedDates = Array.from(sessionDates).sort(); // ISO dates sort naturally
  if (sortedDates.length > 0) {
    dateInput.min = sortedDates[0];
    dateInput.max = sortedDates[sortedDates.length - 1];
 
  }

  // --- Filter function ---
  function filterSessions() {
    const term = keywordBox.value.toLowerCase();
    const selectedCategory = categorySelect.value.toLowerCase();
    const selectedDate = dateInput.value; // YYYY-MM-DD
    const selectedTime = timeSelect.value;
    let visibleCount = 0;

    sessionDivs.forEach(session => {
      const descDiv = session.querySelector(".eventSessionDescription");
      if (!descDiv) return;

      // Keyword search
      let textToSearch = "";
      descDiv.querySelectorAll("span.msdynmkt_personalization").forEach(span => {
        if (span.offsetParent !== null) textToSearch += " " + span.textContent.toLowerCase();
      });

      // Category
      const catMatch = descDiv.textContent.match(/Category:s*(.+)/i);
      const categoryText = catMatch ? catMatch[1].trim().toLowerCase() : "";

      // Session date ISO
      const dateMatch = descDiv.textContent.match(/d{1,2}/d{1,2}/d{4}/);
      const sessionDateISO = dateMatch ? sessionDateToISO(dateMatch[0]) : "";

      // Session time
      const timeMatch = descDiv.textContent.match(/bd{1,2}:d{2}s?[AP]Mb/i);
      const sessionTime = timeMatch ? timeMatch[0].trim() : "";

      const keywordMatch = term === "" || textToSearch.includes(term);
      const categoryMatch = selectedCategory === "" || categoryText === selectedCategory;
      const dateMatchFilter = !selectedDate || sessionDateISO === selectedDate;
      const timeMatchFilter = selectedTime === "" || sessionTime === selectedTime;

      if (keywordMatch && categoryMatch && dateMatchFilter && timeMatchFilter) {
        session.style.setProperty("display", "flex", "important");
        visibleCount++;
      } else {
        session.style.setProperty("display", "none", "important");
      }
    });

    resultsCounter.textContent = visibleCount === 0 ? "No sessions found." :
                                  visibleCount === 1 ? "1 session found." :
                                  `${visibleCount} sessions found.`;
  }

  [keywordBox, categorySelect, dateInput, timeSelect].forEach(el => {
    el.addEventListener("input", filterSessions);
    el.addEventListener("change", filterSessions);
  });

  // Initial run
  filterSessions();
});
</script>

Now let’s tidy things up a bit AND give the person a way to see how many session they have selected already, and a nice button to reset all of the filters rather than needing to manually change them all one by one. The script has a lot of the styling in it (button colours, filter background colours etc) so just tweak it a bit if you want it to show differently.

Click to view in detail
<script>
document.addEventListener("d365mkt-afterformload", function () {
  const sessionsDiv = document.querySelector("div[data-editorblocktype="Sessions"]");
  if (!sessionsDiv) return;

  // --- Counters container ---
  const countersContainer = document.createElement("div");
  countersContainer.style.display = "flex";
  countersContainer.style.flexWrap = "wrap";
  countersContainer.style.alignItems = "center";
  countersContainer.style.gap = "20px";
  countersContainer.style.marginBottom = "10px";
  countersContainer.style.padding = "10px 12px";
  countersContainer.style.background = "#f3f3f3";
  countersContainer.style.border = "1px solid #ccc";
  countersContainer.style.borderRadius = "8px";
  countersContainer.style.boxShadow = "0 2px 5px rgba(0,0,0,0.05)";
  sessionsDiv.insertAdjacentElement("beforebegin", countersContainer);

  // --- Visible sessions counter ---
  const resultsCounter = document.createElement("div");
  resultsCounter.style.fontStyle = "italic";
  resultsCounter.textContent = "";
  countersContainer.appendChild(resultsCounter);

  // --- Selected sessions counter ---
  const selectedCounter = document.createElement("div");
  selectedCounter.style.fontWeight = "bold";
  selectedCounter.textContent = "0 sessions selected";
  countersContainer.appendChild(selectedCounter);

  // --- Filter container ---
  const filterContainer = document.createElement("div");
  filterContainer.style.display = "flex";
  filterContainer.style.flexWrap = "wrap";
  filterContainer.style.alignItems = "center";
  filterContainer.style.gap = "10px";
  filterContainer.style.marginBottom = "15px";
  filterContainer.style.padding = "10px";
  filterContainer.style.background = "#f9f9f9";
  filterContainer.style.border = "1px solid #ccc";
  filterContainer.style.borderRadius = "8px";
  filterContainer.style.boxShadow = "0 2px 5px rgba(0,0,0,0.05)";
  sessionsDiv.insertAdjacentElement("beforebegin", filterContainer);

  // --- Keyword search ---
  const keywordBox = document.createElement("input");
  keywordBox.type = "text";
  keywordBox.placeholder = "Search sessions...";
  keywordBox.style.flex = "1";
  keywordBox.style.minWidth = "150px";
  keywordBox.style.padding = "8px 12px";
  keywordBox.style.border = "1px solid #ccc";
  keywordBox.style.borderRadius = "6px";
  keywordBox.style.boxSizing = "border-box";

  // --- Category dropdown ---
  const categorySelect = document.createElement("select");
  categorySelect.style.flex = "1";
  categorySelect.style.minWidth = "150px";
  categorySelect.style.padding = "8px 12px";
  categorySelect.style.border = "1px solid #ccc";
  categorySelect.style.borderRadius = "6px";
  categorySelect.innerHTML = `<option value="">All Categories</option>`;

  // --- Date picker ---
  const dateInput = document.createElement("input");
  dateInput.type = "date";
  dateInput.style.flex = "1";
  dateInput.style.minWidth = "150px";
  dateInput.style.padding = "8px 12px";
  dateInput.style.border = "1px solid #ccc";
  dateInput.style.borderRadius = "6px";

  // --- Time dropdown ---
  const timeSelect = document.createElement("select");
  timeSelect.style.flex = "1";
  timeSelect.style.minWidth = "120px";
  timeSelect.style.padding = "8px 12px";
  timeSelect.style.border = "1px solid #ccc";
  timeSelect.style.borderRadius = "6px";
  timeSelect.innerHTML = `<option value="">All Times</option>`;

  // --- Reset button ---
  const resetButton = document.createElement("button");
  resetButton.type = "button";
  resetButton.textContent = "Reset Filters";
  resetButton.style.padding = "8px 16px";
  resetButton.style.border = "none";
  resetButton.style.borderRadius = "6px";
  resetButton.style.background = "#3c245c";
  resetButton.style.color = "#fff";
  resetButton.style.cursor = "pointer";
  resetButton.style.flex = "0 0 auto";

  filterContainer.appendChild(keywordBox);
  filterContainer.appendChild(categorySelect);
  filterContainer.appendChild(dateInput);
  filterContainer.appendChild(timeSelect);
  filterContainer.appendChild(resetButton);

  // --- Collect sessions ---
  const sessionDivs = document.querySelectorAll(".eventSession");
  const categories = new Set();
  const times = new Set();
  const sessionDates = new Set();

  function sessionDateToISO(dateStr) {
    const parts = dateStr.split("/");
    return `${parts[2]}-${parts[1].padStart(2,"0")}-${parts[0].padStart(2,"0")}`;
  }

  function timeToNumber(timeStr) {
    const match = timeStr.match(/(d{1,2}):(d{2})s*([AP]M)/i);
    if (!match) return 0;
    let hours = parseInt(match[1],10);
    const minutes = parseInt(match[2],10);
    const ampm = match[3].toUpperCase();
    if (ampm==="PM" && hours<12) hours+=12;
    if (ampm==="AM" && hours===12) hours=0;
    return hours*60+minutes;
  }

  sessionDivs.forEach(session => {
    const descDiv = session.querySelector(".eventSessionDescription");
    if(!descDiv) return;
    const text = descDiv.textContent;

    // Categories
    const catMatch = text.match(/Category:s*(.+)/i);
    if(catMatch) categories.add(catMatch[1].trim());

    // Dates
    const dateMatch = text.match(/d{1,2}/d{1,2}/d{4}/);
    if(dateMatch) sessionDates.add(sessionDateToISO(dateMatch[0]));

    // Times
    const timeMatch = text.match(/bd{1,2}:d{2}s?[AP]Mb/i);
    if(timeMatch) times.add(timeMatch[0].trim());
  });

  // Populate dropdowns
  Array.from(categories).sort().forEach(cat => categorySelect.innerHTML += `<option value="${cat}">${cat}</option>`);
  Array.from(times).sort((a,b)=>timeToNumber(a)-timeToNumber(b)).forEach(t => timeSelect.innerHTML += `<option value="${t}">${t}</option>`);

  // Calendar min/max
  const sortedDates = Array.from(sessionDates).sort();
  if(sortedDates.length>0){
    dateInput.min = sortedDates[0];
    dateInput.max = sortedDates[sortedDates.length-1];
  }

  // --- Filtering function ---
  function filterSessions(){
    const term = keywordBox.value.toLowerCase();
    const selectedCategory = categorySelect.value.toLowerCase();
    const selectedDate = dateInput.value;
    const selectedTime = timeSelect.value;
    let visibleCount = 0;

    sessionDivs.forEach(session=>{
      const descDiv = session.querySelector(".eventSessionDescription");
      if(!descDiv) return;

      let textToSearch = "";
      descDiv.querySelectorAll("span.msdynmkt_personalization").forEach(span=>{
        if(span.offsetParent!==null) textToSearch += " "+span.textContent.toLowerCase();
      });

      const catMatch = descDiv.textContent.match(/Category:s*(.+)/i);
      const categoryText = catMatch ? catMatch[1].trim().toLowerCase() : "";

      const dateMatch = descDiv.textContent.match(/d{1,2}/d{1,2}/d{4}/);
      const sessionDateISO = dateMatch ? sessionDateToISO(dateMatch[0]) : "";

      const timeMatch = descDiv.textContent.match(/bd{1,2}:d{2}s?[AP]Mb/i);
      const sessionTime = timeMatch ? timeMatch[0].trim() : "";

      const keywordMatch = term==="" || textToSearch.includes(term);
      const categoryMatch = selectedCategory==="" || categoryText===selectedCategory;
      const dateMatchFilter = !selectedDate || sessionDateISO===selectedDate;
      const timeMatchFilter = selectedTime==="" || sessionTime===selectedTime;

      if(keywordMatch && categoryMatch && dateMatchFilter && timeMatchFilter){
        session.style.setProperty("display","flex","important");
        visibleCount++;
      } else {
        session.style.setProperty("display","none","important");
      }
    });

    resultsCounter.textContent = visibleCount===0?"No sessions found.":visibleCount===1?"1 session found.":`${visibleCount} sessions found.`;
    updateSelectedCount();
  }

  // --- Selected sessions counter ---
  function updateSelectedCount() {
    let selectedCount = 0;
    sessionDivs.forEach(session => {
      const checkbox = session.querySelector("input[type="checkbox"]");
      if (checkbox && checkbox.checked) selectedCount++;
    });
    selectedCounter.textContent = selectedCount===1?"1 session selected":`${selectedCount} sessions selected`;
  }

  // --- Event listeners ---
  [keywordBox, categorySelect, dateInput, timeSelect].forEach(el=>{
    el.addEventListener("input",filterSessions);
    el.addEventListener("change",filterSessions);
  });

  sessionDivs.forEach(session => {
    const checkbox = session.querySelector("input[type="checkbox"]");
    if (checkbox) checkbox.addEventListener("change", updateSelectedCount);
  });

  resetButton.addEventListener("click", ()=>{
    keywordBox.value="";
    categorySelect.value="";
    dateInput.value="";
    timeSelect.value="";
    filterSessions();
  });

  // Initial run
  filterSessions();
});
</script>

Now we are at what I think is the nicest option but it really depends on when your sessions are. In my example there are sessions on the same date at the same time, so I don’t want someone registering for both in that case. This final script includes ALL of the filters, the number of sessions found, number of sessions selected, a nice reset filters button AND disables a session if a session at the same date/time is already selected. Beautiful!

Click to view in detail
<script>
    document.addEventListener("d365mkt-afterformload", function () {
  const sessionsDiv = document.querySelector("div[data-editorblocktype="Sessions"]");
  if (!sessionsDiv) return;

  // --- Counters container ---
  const countersContainer = document.createElement("div");
  countersContainer.style.display = "flex";
  countersContainer.style.flexWrap = "wrap";
  countersContainer.style.alignItems = "center";
  countersContainer.style.gap = "20px";
  countersContainer.style.marginBottom = "10px";
  countersContainer.style.padding = "10px 12px";
  countersContainer.style.background = "#f3f3f3";
  countersContainer.style.border = "1px solid #ccc";
  countersContainer.style.borderRadius = "8px";
  countersContainer.style.boxShadow = "0 2px 5px rgba(0,0,0,0.05)";
  sessionsDiv.insertAdjacentElement("beforebegin", countersContainer);

  // --- Visible sessions counter ---
  const resultsCounter = document.createElement("div");
  resultsCounter.style.fontStyle = "italic";
  resultsCounter.textContent = "";
  countersContainer.appendChild(resultsCounter);

  // --- Selected sessions counter ---
  const selectedCounter = document.createElement("div");
  selectedCounter.style.fontWeight = "bold";
  selectedCounter.textContent = "0 sessions selected";
  countersContainer.appendChild(selectedCounter);

  // --- Filter container ---
  const filterContainer = document.createElement("div");
  filterContainer.style.display = "flex";
  filterContainer.style.flexWrap = "wrap";
  filterContainer.style.alignItems = "center";
  filterContainer.style.gap = "10px";
  filterContainer.style.marginBottom = "15px";
  filterContainer.style.padding = "10px";
  filterContainer.style.background = "#f9f9f9";
  filterContainer.style.border = "1px solid #ccc";
  filterContainer.style.borderRadius = "8px";
  filterContainer.style.boxShadow = "0 2px 5px rgba(0,0,0,0.05)";
  sessionsDiv.insertAdjacentElement("beforebegin", filterContainer);

  // --- Keyword search ---
  const keywordBox = document.createElement("input");
  keywordBox.type = "text";
  keywordBox.placeholder = "Search sessions...";
  keywordBox.style.flex = "1";
  keywordBox.style.minWidth = "150px";
  keywordBox.style.padding = "8px 12px";
  keywordBox.style.border = "1px solid #ccc";
  keywordBox.style.borderRadius = "6px";
  keywordBox.style.boxSizing = "border-box";

  // --- Category dropdown ---
  const categorySelect = document.createElement("select");
  categorySelect.style.flex = "1";
  categorySelect.style.minWidth = "150px";
  categorySelect.style.padding = "8px 12px";
  categorySelect.style.border = "1px solid #ccc";
  categorySelect.style.borderRadius = "6px";
  categorySelect.innerHTML = `<option value="">All Categories</option>`;

  // --- Date picker ---
  const dateInput = document.createElement("input");
  dateInput.type = "date";
  dateInput.style.flex = "1";
  dateInput.style.minWidth = "150px";
  dateInput.style.padding = "8px 12px";
  dateInput.style.border = "1px solid #ccc";
  dateInput.style.borderRadius = "6px";

  // --- Time dropdown ---
  const timeSelect = document.createElement("select");
  timeSelect.style.flex = "1";
  timeSelect.style.minWidth = "120px";
  timeSelect.style.padding = "8px 12px";
  timeSelect.style.border = "1px solid #ccc";
  timeSelect.style.borderRadius = "6px";
  timeSelect.innerHTML = `<option value="">All Times</option>`;

  // --- Reset button ---
  const resetButton = document.createElement("button");
  resetButton.type = "button";
  resetButton.textContent = "Reset Filters";
  resetButton.style.padding = "8px 16px";
  resetButton.style.border = "none";
  resetButton.style.borderRadius = "6px";
  resetButton.style.background = "#3c245c";
  resetButton.style.color = "#fff";
  resetButton.style.cursor = "pointer";
  resetButton.style.flex = "0 0 auto";

  filterContainer.appendChild(keywordBox);
  filterContainer.appendChild(categorySelect);
  filterContainer.appendChild(dateInput);
  filterContainer.appendChild(timeSelect);
  filterContainer.appendChild(resetButton);

  // --- Collect sessions ---
  const sessionDivs = document.querySelectorAll(".eventSession");
  const categories = new Set();
  const times = new Set();
  const sessionDates = new Set();

  function sessionDateToISO(dateStr) {
    const parts = dateStr.split("/");
    return `${parts[2]}-${parts[1].padStart(2,"0")}-${parts[0].padStart(2,"0")}`;
  }

  function timeToNumber(timeStr) {
    const match = timeStr.match(/(d{1,2}):(d{2})s*([AP]M)/i);
    if (!match) return 0;
    let hours = parseInt(match[1],10);
    const minutes = parseInt(match[2],10);
    const ampm = match[3].toUpperCase();
    if (ampm==="PM" && hours<12) hours+=12;
    if (ampm==="AM" && hours===12) hours=0;
    return hours*60+minutes;
  }

  sessionDivs.forEach(session => {
    const descDiv = session.querySelector(".eventSessionDescription");
    if(!descDiv) return;
    const text = descDiv.textContent;

    // Categories
    const catMatch = text.match(/Category:s*(.+)/i);
    if(catMatch) categories.add(catMatch[1].trim());

    // Dates
    const dateMatch = text.match(/d{1,2}/d{1,2}/d{4}/);
    if(dateMatch) sessionDates.add(sessionDateToISO(dateMatch[0]));

    // Times
    const timeMatch = text.match(/bd{1,2}:d{2}s?[AP]Mb/i);
    if(timeMatch) times.add(timeMatch[0].trim());
  });

  // Populate dropdowns
  Array.from(categories).sort().forEach(cat => categorySelect.innerHTML += `<option value="${cat}">${cat}</option>`);
  Array.from(times).sort((a,b)=>timeToNumber(a)-timeToNumber(b)).forEach(t => timeSelect.innerHTML += `<option value="${t}">${t}</option>`);

  // Calendar min/max
  const sortedDates = Array.from(sessionDates).sort();
  if(sortedDates.length>0){
    dateInput.min = sortedDates[0];
    dateInput.max = sortedDates[sortedDates.length-1];
  }

  // --- Filtering function ---
  function filterSessions(){
    const term = keywordBox.value.toLowerCase();
    const selectedCategory = categorySelect.value.toLowerCase();
    const selectedDate = dateInput.value;
    const selectedTime = timeSelect.value;
    let visibleCount = 0;

    sessionDivs.forEach(session=>{
      const descDiv = session.querySelector(".eventSessionDescription");
      if(!descDiv) return;

      let textToSearch = "";
      descDiv.querySelectorAll("span.msdynmkt_personalization").forEach(span=>{
        if(span.offsetParent!==null) textToSearch += " "+span.textContent.toLowerCase();
      });

      const catMatch = descDiv.textContent.match(/Category:s*(.+)/i);
      const categoryText = catMatch ? catMatch[1].trim().toLowerCase() : "";

      const dateMatch = descDiv.textContent.match(/d{1,2}/d{1,2}/d{4}/);
      const sessionDateISO = dateMatch ? sessionDateToISO(dateMatch[0]) : "";

      const timeMatch = descDiv.textContent.match(/bd{1,2}:d{2}s?[AP]Mb/i);
      const sessionTime = timeMatch ? timeMatch[0].trim() : "";

      const keywordMatch = term==="" || textToSearch.includes(term);
      const categoryMatch = selectedCategory==="" || categoryText===selectedCategory;
      const dateMatchFilter = !selectedDate || sessionDateISO===selectedDate;
      const timeMatchFilter = selectedTime==="" || sessionTime===selectedTime;

      if(keywordMatch && categoryMatch && dateMatchFilter && timeMatchFilter){
        session.style.setProperty("display","flex","important");
        visibleCount++;
      } else {
        session.style.setProperty("display","none","important");
      }
    });

    resultsCounter.textContent = visibleCount===0?"No sessions found.":visibleCount===1?"1 session found.":`${visibleCount} sessions found.`;
    updateSelectedCount();
    enforceNoDoubleBooking();
  }

  // --- Selected sessions counter ---
  function updateSelectedCount() {
    let selectedCount = 0;
    sessionDivs.forEach(session => {
      const checkbox = session.querySelector("input[type="checkbox"]");
      if (checkbox && checkbox.checked) selectedCount++;
    });
    selectedCounter.textContent = selectedCount===1?"1 session selected":`${selectedCount} sessions selected`;
  }

  // --- No double booking enforcement ---
function enforceNoDoubleBooking() {
  const selectedDateTime = new Map();
  
  sessionDivs.forEach(session => {
    const checkbox = session.querySelector("input[type="checkbox"]");
    const descDiv = session.querySelector(".eventSessionDescription");
    if (!checkbox || !descDiv) return;

    // Get date
    const dateMatch = descDiv.textContent.match(/d{1,2}/d{1,2}/d{4}/);
    const sessionDate = dateMatch ? sessionDateToISO(dateMatch[0]) : "";

    // Get time
    const timeMatch = descDiv.textContent.match(/bd{1,2}:d{2}s?[AP]Mb/i);
    const sessionTime = timeMatch ? timeMatch[0].trim() : "";

    if (checkbox.checked && sessionDate && sessionTime) {
      selectedDateTime.set(`${sessionDate} ${sessionTime}`, true);
    }
  });

  sessionDivs.forEach(session => {
    const checkbox = session.querySelector("input[type="checkbox"]");
    const descDiv = session.querySelector(".eventSessionDescription");
    if (!checkbox || !descDiv) return;

    const dateMatch = descDiv.textContent.match(/d{1,2}/d{1,2}/d{4}/);
    const sessionDate = dateMatch ? sessionDateToISO(dateMatch[0]) : "";

    const timeMatch = descDiv.textContent.match(/bd{1,2}:d{2}s?[AP]Mb/i);
    const sessionTime = timeMatch ? timeMatch[0].trim() : "";

    const key = `${sessionDate} ${sessionTime}`;

    if (!checkbox.checked && selectedDateTime.has(key)) {
      checkbox.disabled = true;
      session.style.opacity = "0.5";
      session.style.pointerEvents = "none";
      session.style.background = "#e3e0f0";
      session.style.transition = "background 0.3s, opacity 0.3s";
    } else {
      checkbox.disabled = false;
      session.style.opacity = "1";
      session.style.pointerEvents = "auto";
      session.style.background = "#f3f3f3";
    }
  });
}

  // --- Event listeners ---
  [keywordBox, categorySelect, dateInput, timeSelect].forEach(el=>{
    el.addEventListener("input",filterSessions);
    el.addEventListener("change",filterSessions);
  });

  sessionDivs.forEach(session => {
    const checkbox = session.querySelector("input[type="checkbox"]");
    if (checkbox) checkbox.addEventListener("change", () => {
      updateSelectedCount();
      enforceNoDoubleBooking();
    });
  });

  resetButton.addEventListener("click", ()=>{
    keywordBox.value="";
    categorySelect.value="";
    dateInput.value="";
    timeSelect.value="";
    filterSessions();
  });

  // Initial run
  filterSessions();
});
  </script>

I hope these scripts help. Some things to keep in mind if you go down this path and want to add some search and filtering controls to your event registration with sessions form.

  • Using this option, you will need to do a redirect on your form to a landing page for the best possible user experience. This is because when showing and hiding the sessions, only the ones selected that are still visible will show on the confirmation if you keep that on the same page. Use this post here to format what your redirect process will look like
  • Do NOT use all of the scripts. I thought it would be helpful to break things down so you can see what each one will do, but only select one of them
  • I cannot make updates for every little scenario or change you want. By all means post your comment about what you are looking for, but I may not be able to add every nuance in to this post
  • Hope it helps improve the event registration experience for your attendees!
  • Finally, if you want to try it out, you can review an example here.

Original Post http://meganvwalker.com/search-and-filtering-of-sessions-on-event-form/

0 Votes: 0 Upvotes, 0 Downvotes (0 Points)

Leave a reply

Follow
Search
Popular Now
Loading

Signing-in 3 seconds...

Signing-up 3 seconds...