Introduction
I decided to develop a theme for realtors in my city as A LOT of realtors in my city are still using wordpress and I wanted to create an alternative for them.
The Plan
Here is the game plan:
- Find an SSG that will work
- Build out the pages first in HTML, SASS and JS
- Build out the templates
- Find a CMS that will work with the themes and SSG
For this article, I am going to cover the SSG and Pages
The SSG
For the static site generator, I decided to go with Hugo. It is simple to use and the templating engine is awesome! After reading the docs I think it will work with what I need.
Building out the pages
So, the main pages I require are:
- Home
- Listings
- A listing
- Staff Page
- Contact Us
Home Page
Listings
For the listing page I will need to set up a filterable gallery.
The Gallery
I decided to go witht thise style of gallery I think it looks nice. Next to add some filtering
The filters
// File#: _3_advanced-filter
// Usage: codyhouse.co/license
(function() {
// the AdvFilter object is used to handle:
// - number of results
// - form reset
// - filtering sections label (to show a preview of the option selected by the users)
var AdvFilter = function(element) {
this.element = element;
this.form = this.element.getElementsByClassName('js-adv-filter__form');
this.resultsList = this.element.getElementsByClassName('js-adv-filter__gallery')[0];
this.resultsCount = this.element.getElementsByClassName('js-adv-filter__results-count');
initAdvFilter(this);
};
function initAdvFilter(filter) {
if(filter.form.length > 0) {
// reset form
filter.form[0].addEventListener('reset', function(event){
setTimeout(function(){
resetFilters(filter);
resetGallery(filter);
});
});
// update section labels on form change
filter.form[0].addEventListener('change', function(event){
var section = event.target.closest('.js-adv-filter__item');
if(section) resetSelection(filter, section);
else if( Util.is(event.target, '.js-adv-filter__form') ) {
// reset the entire form lables
var sections = filter.form[0].getElementsByClassName('js-adv-filter__item');
for(var i = 0; i < sections.length; i++) resetSelection(filter, sections[i]);
}
});
}
// reset results count
if(filter.resultsCount.length > 0) {
filter.resultsList.addEventListener('filter-selection-updated', function(event){
updateResultsCount(filter);
});
}
};
function resetFilters(filter) {
// check if there are custom form elemets - reset appearance
// custom select
var customSelect = filter.element.getElementsByClassName('js-select');
if(customSelect.length > 0) {
for(var i = 0; i < customSelect.length; i++) customSelect[i].dispatchEvent(new CustomEvent('select-updated'));
}
// custom slider
var customSlider = filter.element.getElementsByClassName('js-slider');
if(customSlider.length > 0) {
for(var i = 0; i < customSlider.length; i++) customSlider[i].dispatchEvent(new CustomEvent('slider-updated'));
}
};
function resetSelection(filter, section) {
// change label value based on input types
var labelSelection = section.getElementsByClassName('js-adv-filter__selection');
if(labelSelection.length == 0) return;
// select
var select = section.getElementsByTagName('select');
if(select.length > 0) {
labelSelection[0].textContent = getSelectLabel(section, select[0]);
return;
}
// input number
var number = section.querySelectorAll('input[type="number"]');
if(number.length > 0) {
labelSelection[0].textContent = getNumberLabel(section, number);
return;
}
// input range
var slider = section.querySelectorAll('input[type="range"]');
if(slider.length > 0) {
labelSelection[0].textContent = getSliderLabel(section, slider);
return;
}
// radio/checkboxes
var radio = section.querySelectorAll('input[type="radio"]'),
checkbox = section.querySelectorAll('input[type="checkbox"]');
if(radio.length > 0) {
labelSelection[0].textContent = getInputListLabel(section, radio);
return;
} else if(checkbox.length > 0) {
labelSelection[0].textContent = getInputListLabel(section, checkbox);
return;
}
};
function getSelectLabel(section, select) {
if(select.multiple) {
var label = '',
counter = 0;
for (var i = 0; i < select.options.length; i++) {
if(select.options[i].selected) {
label = label + '' + select.options[i].text;
counter = counter + 1;
}
if(counter > 1) label = section.getAttribute('data-multi-select-text').replace('{n}', counter);
}
return label;
} else {
return select.options[select.selectedIndex].text;
}
};
function getNumberLabel(section, number) {
var counter = 0;
for(var i = 0; i < number.length; i++) {
if(number[i].value != number[i].min) counter = counter + 1;
}
if(number.length > 1) { // multiple input number in this section
if(counter > 0) {
return section.getAttribute('data-multi-select-text').replace('{n}', counter);
} else {
return section.getAttribute('data-default-text');
}
} else {
if(number[0].value == number[0].min) return section.getAttribute('data-default-text');
else return section.getAttribute('data-number-format').replace('{n}', number[0].value);
}
};
function getSliderLabel(section, slider) {
var label = '',
labelFormat = section.getAttribute('data-number-format');
for(var i = 0; i < slider.length; i++) {
if(i != 0 ) label = label+' - ';
label = label + labelFormat.replace('{n}', slider[i].value);
}
return label;
};
function getInputListLabel(section, inputs) {
var counter = 0;
label = '';
for(var i = 0; i < inputs.length; i++) {
if(inputs[i].checked) {
var labelElement = inputs[i].parentNode.getElementsByTagName('label');
if(labelElement.length > 0) label = labelElement[0].textContent;
counter = counter + 1;
}
}
if(counter > 1) return section.getAttribute('data-multi-select-text').replace('{n}', counter);
else if(counter == 0 ) return section.getAttribute('data-default-text');
else return label;
};
function resetGallery(filter) {
// emit change event + reset filtering
filter.form[0].dispatchEvent(new CustomEvent('change'));
filter.resultsList.dispatchEvent(new CustomEvent('update-filter-results'));
};
function updateResultsCount(filter) {
var resultItems = filter.resultsList.children,
counter = 0;
for(var i = 0; i < resultItems.length; i++) {
if(isVisible(resultItems[i])) counter = counter + 1;
}
filter.resultsCount[0].textContent = counter;
};
function isVisible(element) {
return (element.offsetWidth || element.offsetHeight || element.getClientRects().length);
};
//initialize the AdvFilter objects
var advFilter = document.getElementsByClassName('js-adv-filter');
if( advFilter.length > 0 ) {
for( var i = 0; i < advFilter.length; i++) {
(function(i){new AdvFilter(advFilter[i]);})(i);
}
}
// Remove the code below if you want to use a custom filtering function (e.g., you need to fetch your results from a database)
// The code below is used for filtering of page content (animation of DOM elements, no fetching results from database).
// It uses the Filter component (https://codyhouse.co/ds/components/app/filter) - you can modify the custom filtering functions based on your needs
// Check the info page of the component for info on how to use it: https://codyhouse.co/ds/components/info/filter
var gallery = document.getElementById('adv-filter-gallery');
if(gallery) {
new Filter({
element: gallery, // this is your gallery element
priceRange: function(items){ // this is the price custom function
var filteredArray = [],
minVal = document.getElementById('slider-min-value').value,
maxVal = document.getElementById('slider-max-value').value;
for(var i = 0; i < items.length; i++) {
var price = parseInt(items[i].getAttribute('data-price'));
filteredArray[i] = (price >= minVal) && (price <= maxVal);
}
return filteredArray;
},
indexValue: function(items){ // this is the index custom function
var filteredArray = [],
value = document.getElementById('index-value').value;
for(var i = 0; i < items.length; i++) {
var index = parseInt(items[i].getAttribute('data-sort-index'));
filteredArray[i] = index >= value;
}
return filteredArray;
}
});
}
}());
A Listing
Images
Tabs
I set up some tabs to host all of the main content for the listing. I think this will help separate everything and make it easier for the user to navigate.
Information
There is a lot of information that goes into the listing. So I used a template for a FAQ section and separated all the information within it.
Top comments (1)
Where is the code for this?