CMS Development

Woodsy
Top Contributor

Add a Keyword Filter / Search

SOLVE

Hi, I have a blog page that is filtered by topic. Is there a way to add a search bar next to the topic filter that searches the blog listing for keywords and then displays the results in the same card format?
Here is the live page: Channel Marketing Blog | 360insights

Topic filter code:

 

<div class="blog-listing-bar__topics">
<form>
<label for="topics">{% text "browse_by_topic_title" label="Browse by Topic Title", value="Browse by topic" %}</label>
<select name="topics" id="topics" class="mb-none">
{% set topics = blog_topics('default', 250) %}
<option value="{{ group.absolute_url }}">All Topics</option>
{% for topic in topics %}
<option value="{{ group.absolute_url }}/tag/{{topic.slug}}" {% if topic.slug == tag %}selected{% endif %}>{{ topic }}</option>
{% endfor %}
</select>
</form>
</div>

 


Card display code:

 

<div class="cards cards--3">
{% for content in contents %}
{% if content.widgets.resource_url.body.value %}
{% set postUrl = content.widgets.resource_url.body.value %}
{% else %}
{% set postUrl = content.absolute_url %}
{% endif %}
{% if not simple_list_page %}
<a href="{{ postUrl }}" class="card {% for topic in content.topic_list %} {{ topic.slug }}{% if not loop.last %}{% endif %}{% endfor %}" data-topics="{% for topic in content.topic_list %}{{ topic.slug }}{% if not loop.last %}, {% endif %}{% endfor %}">
<div class="card__image">
<img src="{{ content.featured_image }}" alt="" loading="lazy">
</div>

<div class="card__details">
<div class="card__title">
{{ content.name }}
</div>
{% if card.description %}
<div class="card__desc">
{{ content.post_list_content|safe|striptags|truncatehtml(150, '...', false) }}
</div>
{% endif %}
<span class="button button--arrow button--notext"></span>
</div>
</a>
{% else %}
<h2 class="blog-listing__post-simple"><a href="{{ content.absolute_url }}">{{ content.name }}</a></h2>
{% endif %}
{% endfor %}
</div>

 


Thanks

0 Upvotes
1 Accepted solution
BarryGrennan
Solution
Top Contributor

Add a Keyword Filter / Search

SOLVE

In that case your best option is something like:

- Create a custom search module that uses the html and javascript that matches the html of your cards

- Insert that module in your blog listing page template

- Create a custom search input on the page that would use the same url as the blog list page, like 

<div class="hs-search-field"> 
<div class="hs-search-field__bar">
<form action="[BLOG_LIST_PAGE_URL_GOES_HERE]">
<label for="term">Search Blog</label>
<input type="text" class="hs-search-field__input" name="term" autocomplete="off" aria-label="Field Label | Search" placeholder="Search Blog">
<input type="hidden" name="type" value="BLOG_POST">
<input type="hidden" name="type" value="LISTING_PAGE">
<input type="hidden" name="is_search_results" value="true">
</form>
</div>
<ul class="hs-search-field__suggestions"></ul>
</div>

 

- Then in the template layout you'd use the "is_search_results" field I inserted above to detect that search reults are being shown and hide the regular content loop

{% if request.query_dict.is_search_results == "true" %}

[LAYOUT OF THE TEMPLATE WITH SEARCH RESULTS]

{% else %}

[NORMAL LAYOUT OF THE TEMPLATE]

{% endif %}

 

Note in the case you'll also need to change the url the custom search module uses for the "Previous" and "Next" links.

 


profile2022aBarry Grennan

Freelance HubSpot CMS Developer

Website | Contact | LinkedIn

 

 

View solution in original post

8 Replies 8
Woodsy
Top Contributor

Add a Keyword Filter / Search

SOLVE

Hi Barry,

If I select a template to use in https://app.hubspot.com/settings/[YOUR-PORTAL-ID-GOES-HERE]/website/pages/all-domains/system-pages this would change any global search that we add in the furture. We want the main global search to display results as a header and first couple of line of any content that matches keywords. I want this blog search to only bring back matching blogs in the same visual card style. If I tailor the search page to display the blog results as cards by calling the blog.css and use that template here https://app.hubspot.com/settings/[YOUR-PORTAL-ID-GOES-HERE]/website/pages/all-domains/system-pages it will try and display everything that matches the keywords as a card. How would I stop the global search when we implement it from using the blog.css blog card style?

Thanks

0 Upvotes
BarryGrennan
Solution
Top Contributor

Add a Keyword Filter / Search

SOLVE

In that case your best option is something like:

- Create a custom search module that uses the html and javascript that matches the html of your cards

- Insert that module in your blog listing page template

- Create a custom search input on the page that would use the same url as the blog list page, like 

<div class="hs-search-field"> 
<div class="hs-search-field__bar">
<form action="[BLOG_LIST_PAGE_URL_GOES_HERE]">
<label for="term">Search Blog</label>
<input type="text" class="hs-search-field__input" name="term" autocomplete="off" aria-label="Field Label | Search" placeholder="Search Blog">
<input type="hidden" name="type" value="BLOG_POST">
<input type="hidden" name="type" value="LISTING_PAGE">
<input type="hidden" name="is_search_results" value="true">
</form>
</div>
<ul class="hs-search-field__suggestions"></ul>
</div>

 

- Then in the template layout you'd use the "is_search_results" field I inserted above to detect that search reults are being shown and hide the regular content loop

{% if request.query_dict.is_search_results == "true" %}

[LAYOUT OF THE TEMPLATE WITH SEARCH RESULTS]

{% else %}

[NORMAL LAYOUT OF THE TEMPLATE]

{% endif %}

 

Note in the case you'll also need to change the url the custom search module uses for the "Previous" and "Next" links.

 


profile2022aBarry Grennan

Freelance HubSpot CMS Developer

Website | Contact | LinkedIn

 

 

BarryGrennan
Top Contributor

Add a Keyword Filter / Search

SOLVE

Hi @Woodsy , 

 

You'd use the hubspot Search Input Module to add the searchbar

{% module "search_input" path="@hubspot/search_input" %}

 

In terms of how the results are output, I can see from a test on your site that they aren't styled

https://www.360insights.com/hs-search-results?term=test&type=BLOG_POST

 

So you'd need to create a custom version of the @hubspot/search_results module and insert it in your search results template.

 


profile2022aBarry Grennan

Freelance HubSpot CMS Developer

Website | Contact | LinkedIn

 

 

0 Upvotes
Woodsy
Top Contributor

Add a Keyword Filter / Search

SOLVE

Hi, would I use something like this:

<div class="hs-search-field"> 
<div class="hs-search-field__bar">
<form action="/{{ site_settings.content_search_results_page_path }}">
<label for="term">Search Blog</label>
<input type="text" class="hs-search-field__input" name="term" autocomplete="off" aria-label="Field Label | Search" placeholder="Search Blog">
<input type="hidden" name="type" value="BLOG_POST">
<input type="hidden" name="type" value="LISTING_PAGE">
</form>
</div>
<ul class="hs-search-field__suggestions"></ul>
</div>


I would like the results to populate where the current blog listing is, similar to how the current topic filter reloads the same page with the filtered blogs. In this instance it would reload the same page but with only the keyword relevent blogs appearing.

 

If I use my code above it loads a the results into a separte page due to this code

{{ site_settings.content_search_results_page_path }}



Thanks

0 Upvotes
BarryGrennan
Top Contributor

Add a Keyword Filter / Search

SOLVE

Hi @Woodsy 

 

What loads on tag pages (e.g. https://www.360insights.com/blog/tag/fund-management) is your blog-list template.

What loads on search results page (e.g. https://www.360insights.com/hs-search-results?term=test&type=BLOG_POST) is your search results template.

What you'd need to do is edit/create a new search results template that includes the same modules as the blog-list template (e.g. hero, blog-listing-bar, etc.)

 


profile2022aBarry Grennan

Freelance HubSpot CMS Developer

Website | Contact | LinkedIn

 

 

0 Upvotes
Woodsy
Top Contributor

Add a Keyword Filter / Search

SOLVE

Hi Barry,

So I am effectivly creating another page with exactly the same framework and content but with the search results module replacing where the current blog listing is? The standard results list will then just pull in the blog header and first couple of lines of the blog. I would then need to get the results listing to pull the thumbnail and style the list as cards.

0 Upvotes
BarryGrennan
Top Contributor

Add a Keyword Filter / Search

SOLVE

Well no. You can't create a "page" for search results. You have to use a template.
You select which template to use in https://app.hubspot.com/settings/[YOUR-PORTAL-ID-GOES-HERE]/website/pages/all-domains/system-pages

You'd build your search results template to mimmick your blog-listings template

 

You need to create a custom search results module to style the results.

 

The default hubspot search results module (@hubspot/search_results) uses this html:

 

 

<div class="hs-search-results">
  <template class="hs-search-results__template">
    <li>
      {% if module.display_featured_images %}
        <div class="hs-search-results__featured-image">
          <img src="">
        </div>
      {% endif %}
      <a href="#" class="hs-search-results__title">Content Title</a>
      <p class="hs-search-results__description">Description</p>
    </li>
  </template>
  <ul id="hsresults" class="hs-search-results__listing"></ul>
  <div class="hs-search-results__pagination" data-search-path="{{ site_settings.content_search_results_page_path }}">
    <a href="" class="hs-search-results__prev-page"></a>
    <a href="" class="hs-search-results__next-page"></a>
  </div>
</div>

 

 

and this javascript

 

 

var hsResultsPage = function(_resultsClass) {
  function buildResultsPage(_instance) {
    var resultTemplate = _instance.querySelector(
      '.hs-search-results__template'
    );
    var resultsSection = _instance.querySelector('.hs-search-results__listing');
    var searchPath = _instance
      .querySelector('.hs-search-results__pagination')
      .getAttribute('data-search-path');
    var prevLink = _instance.querySelector('.hs-search-results__prev-page');
    var nextLink = _instance.querySelector('.hs-search-results__next-page');

    var searchParams = new URLSearchParams(window.location.search.slice(1));

    /**
     * v1 of the search input module uses the `q` param for the search query.
     * This check is a fallback for a mixed v0 of search results and v1 of search input.
     */

    if (searchParams.has('q')) {
      searchParams.set('term', searchParams.get('q'));
      searchParams.delete('q');
    }

    function getTerm() {
      return searchParams.get('term') || '';
    }
    function getOffset() {
      return parseInt(searchParams.get('offset')) || 0;
    }
    function getLimit() {
      return parseInt(searchParams.get('limit'));
    }
    function addResult(title, url, description, featuredImage) {
      var newResult = document.importNode(resultTemplate.content, true);
      function isFeaturedImageEnabled() {
        if (
          newResult.querySelector('.hs-search-results__featured-image > img')
        ) {
          return true;
        }
      }
      newResult.querySelector('.hs-search-results__title').innerHTML = title;
      newResult.querySelector('.hs-search-results__title').href = url;
      newResult.querySelector(
        '.hs-search-results__description'
      ).innerHTML = description;
      if (typeof featuredImage !== 'undefined' && isFeaturedImageEnabled()) {
        newResult.querySelector(
          '.hs-search-results__featured-image > img'
        ).src=featuredImage;
      }
      resultsSection.appendChild(newResult);
    }
    function fillResults(results) {
      results.results.forEach(function(result, i) {
        addResult(
          result.title,
          result.url,
          result.description,
          result.featuredImageUrl
        );
      });
    }
    function emptyPagination() {
      prevLink.innerHTML = '';
      nextLink.innerHTML = '';
    }
    function emptyResults(searchedTerm) {
      resultsSection.innerHTML =
        '<div class="hs-search__no-results"><p>Sorry. There are no results for "' +
        searchedTerm +
        '"</p>' +
        '<p>Try rewording your query, or browse through our site.</p></div>';
    }
    function setSearchBarDefault(searchedTerm) {
      var searchBars = document.querySelectorAll('.hs-search-field__input');
      Array.prototype.forEach.call(searchBars, function(el) {
        el.value = searchedTerm;
      });
    }
    function httpRequest(term, offset) {
      var SEARCH_URL = '/_hcms/search?';
      var requestUrl = SEARCH_URL + searchParams + '&analytics=true';
      var request = new XMLHttpRequest();

      request.open('GET', requestUrl, true);
      request.onload = function() {
        if (request.status >= 200 && request.status < 400) {
          var data = JSON.parse(request.responseText);
          setSearchBarDefault(data.searchTerm);
          if (data.total > 0) {
            fillResults(data);
            paginate(data);
          } else {
            emptyResults(data.searchTerm);
            emptyPagination();
          }
        } else {
          console.error('Server reached, error retrieving results.');
        }
      };
      request.onerror = function() {
        console.error('Could not reach the server.');
      };
      request.send();
    }
    function paginate(results) {
      var updatedLimit = getLimit() || results.limit;

      function hasPreviousPage() {
        return results.page > 0;
      }
      function hasNextPage() {
        return results.offset <= results.total - updatedLimit;
      }

      if (hasPreviousPage()) {
        var prevParams = new URLSearchParams(searchParams.toString());
        prevParams.set(
          'offset',
          results.page * updatedLimit - parseInt(updatedLimit)
        );
        prevLink.href = '/' + searchPath + '?' + prevParams;
        prevLink.innerHTML = '&lt; Previous page';
      } else {
        prevLink.parentNode.removeChild(prevLink);
      }

      if (hasNextPage()) {
        var nextParams = new URLSearchParams(searchParams.toString());
        nextParams.set(
          'offset',
          results.page * updatedLimit + parseInt(updatedLimit)
        );
        nextLink.href = '/' + searchPath + '?' + nextParams;
        nextLink.innerHTML = 'Next page &gt;';
      } else {
        nextLink.parentNode.removeChild(nextLink);
      }
    }
    var getResults = (function() {
      if (getTerm()) {
        httpRequest(getTerm(), getOffset());
      } else {
        emptyPagination();
      }
    })();
  }
  (function() {
    var searchResults = document.querySelectorAll(_resultsClass);
    Array.prototype.forEach.call(searchResults, function(el) {
      buildResultsPage(el);
    });
  })();
};

if (
  document.attachEvent
    ? document.readyState === 'complete'
    : document.readyState !== 'loading'
) {
  var resultsPages = hsResultsPage('div.hs-search-results');
} else {
  document.addEventListener('DOMContentLoaded', function() {
    var resultsPages = hsResultsPage('div.hs-search-results');
  });
}

 

 

To return the search results.

 

You would need to clone that module and edit the html and javascript  to match your blog layout.

 

It looks like your blog is using a blog.css file you'd likely want to add that to your search results page template also, with something like:

 

 

{% set template_css = "../../css/templates/blog.css" %}

 

 

 


profile2022aBarry Grennan

Freelance HubSpot CMS Developer

Website | Contact | LinkedIn

 

 

0 Upvotes
Woodsy
Top Contributor

Add a Keyword Filter / Search

SOLVE

Hi Barry, is there a way I can create a staging or test area for this blog listing page? I would need to check that the code is working so that I can test the listing page is pulling the results and displaying them correctly. Thanks

0 Upvotes