Custom dropdown

Options

To access options, add data-attribute to .dropdown div.

  • data-dropdown-close-on-click: Close dropdown when element inside is clicked
  • data-dropdown-set-header: Set value of clicked element, as the dropdown header
  • Close div when click outside: Copy code from "JavaScript - ClickHandlerModule.ts" from the bottom of this page

Dependencies

Remember to remove unused functions and code

ul li list

  • data-dropdown-close-on-click
  • data-dropdown-set-header

Custom checkbox list

Custom radio list

Html


 <!-- Dropdown with 
    data-dropdown-close-on-click, 
    data-dropdown-set-header, 
    data-close-attr 
-->
<div class="dropdown" data-dropdown data-dropdown-close-on-click data-dropdown-set-header data-close-attr>
    <div class="dropdown__header" data-dropdown-header>
        <span class="dropdown__header-text">Dropdown</span>
    </div>
    <div class="dropdown__content" data-dropdown-content>
        <ul class="reset-ul dropdown__content-list">
            <li class="dropdown__content-list-item">
                Item 1
            </li>
            <li class="dropdown__content-list-item">
                Item 2
            </li>
            <li class="dropdown__content-list-item">
                Item 3
            </li>
        </ul>
    </div>
</div>
<!-- Dropdown with input checkboxes and data-close-attr -->
<div class="dropdown" data-dropdown data-close-attr>
	<div class="dropdown__header" data-dropdown-header>
		<span class="dropdown__header-text">Dropdown</span>
	</div>
	<div class="dropdown__content" data-dropdown-content>
		<form>
			<ul class="reset-ul dropdown__content-list">
				<li class="dropdown__content-list-item">
					<div class="input-element input-element--dark">
						<input type="checkbox" name="checkbox" class="input-element__checkbox" id="checkbox" />
						<label for="checkbox" class="input-element__label">Checkbox 1</label>
					</div>
				</li>
				<li class="dropdown__content-list-item">
					<div class="input-element input-element--dark">
						<input type="checkbox" name="checkbox" class="input-element__checkbox" id="checkbox2" />
						<label for="checkbox2" class="input-element__label">Checkbox 2</label>
					</div>
				</li>
				<li class="dropdown__content-list-item">
					<div class="input-element input-element--dark">
						<input type="checkbox" name="checkbox" class="input-element__checkbox" id="checkbox3" />
						<label for="checkbox3" class="input-element__label">Checkbox 2</label>
					</div>
				</li>
			</ul>
		</form>
	</div>
</div>
<!-- Dropdown with input radio buttons and data-close-attr -->
<div class="dropdown" data-dropdown data-close-attr>
	<div class="dropdown__header" data-dropdown-header>
		<span class="dropdown__header-text">Dropdown</span>
	</div>
	<div class="dropdown__content" data-dropdown-content>
		<form>
			<ul class="reset-ul dropdown__content-list">
				<li class="dropdown__content-list-item">
					<div class="input-element input-element--dark">
						<input type="radio" name="radio" class="input-element__radio" id="radio" />
						<label for="radio" class="input-element__label">radio 1</label>
					</div>
				</li>
				<li class="dropdown__content-list-item">
					<div class="input-element input-element--dark">
						<input type="radio" name="radio" class="input-element__radio" id="radio2" />
						<label for="radio2" class="input-element__label">radio 2</label>
					</div>
				</li>
				<li class="dropdown__content-list-item">
					<div class="input-element input-element--dark">
						<input type="radio" name="radio" class="input-element__radio" id="radio3" />
						<label for="radio3" class="input-element__label">radio 2</label>
					</div>
				</li>
			</ul>
		</form>
	</div>
</div>

Scss

 
		
// local vars
$dropdown-spacing: 2.2rem;
$icon-size: 1.1rem;
$icon-size--devices: 1.4rem;

.dropdown {
	position: relative;
	z-index: 2;
	//header
	&__header {
		padding: $dropdown-spacing;
		background-color: $color-gray-wild-sand;
		font-weight: 600;
		position: relative;
		cursor: pointer;
	}

	&__header:after {
		content: '';
		position: absolute;
		top: 2.5rem;
		right: $dropdown-spacing;
		width: $icon-size;
		height: $icon-size;
		background-image: url('~@/assets/images/icons/angle.svg');
		background-size: 100% 100%;
		transform: rotate(90deg) scale(1);
		transition: transform $transition--fast;
	}

	&__header-text {
		line-height: 1;
		font-size: 1.6rem;
	}
	// content
	&__content {
		position: absolute;
		background-color: $color-gray-wild-sand;
		left: 0;
		right: 0;
		max-height: 0;
		overflow: hidden;
		transition: $transition--fast;
	}
	// content list
	&__content-list {
		margin: 0;
		padding-left: 0;
	}

	&__content-list-item {
		padding: 0 $dropdown-spacing;
		font-size: 1.4rem;
		cursor: pointer;
		opacity: 0;
		padding-bottom: 1.5rem;
		transform: translate3d(0,-2rem,0);
		transition: $transition;

		&:last-child {
			padding-bottom: 2.5rem
		}
	}
	// content list loop
	@for $i from 1 through 14 {
		&__content-list-item:nth-child(#{$i}) {
			transition-delay: $i*.04s;
		}
	}
}
// hover transition

.dropdown__header:hover {
	&:after {
		transform: rotate(90deg) scale(1.4);
	}
}
// active transition
.dropdown.is-active {
	.dropdown__header:hover {
		&:after {
			transform: rotate(-90deg) scale(1.4);
		}
	}

	.dropdown__header:after {
		transform: rotate(-90deg) scale(1);
	}

	.dropdown__content {
		max-height: 40rem;
		transition: $transition--slow;
	}

	.dropdown__content-list-item {
		opacity: 1;
		transform: translate3d(0,0,0);
	}
}
// overwrite custom checkbox margin
.dropdown {
	.input-element {
		margin: .28rem 0;
	}
}
// tablet and down
@include viewport-large {
	.dropdown__header:after {
		top: 2.48rem;
		width: $icon-size--devices;
		height: $icon-size--devices;
	}

	.dropdown__header-text {
		line-height: 1;
		font-size: 1.8rem;
	}
}

JavaScript - DropdownModule.ts

 
	
export class Dropdown {
	dropdownItemList: NodeList = document.querySelectorAll("[data-dropdown]");
	dropdownItemListArray: HTMLElement[] = Array.prototype.slice.call(this.dropdownItemList);
	dropdownItemHeaderList: NodeList = document.querySelectorAll("[data-dropdown-header]");
	dropdownItemArray: HTMLElement[] = Array.prototype.slice.call(this.dropdownItemHeaderList);
	dropdownItemContentList: NodeList = document.querySelectorAll("[data-dropdown-close-on-click] [data-dropdown-content]");
	dropdownItemContentArray: HTMLElement[] = Array.prototype.slice.call(this.dropdownItemContentList);
	dropdownItemContentSetHeaderList: NodeList = document.querySelectorAll("[data-dropdown-set-header] [data-dropdown-content] li");
	dropdownItemContentSetHeaderArray: HTMLElement[] = Array.prototype.slice.call(this.dropdownItemContentSetHeaderList);

	activeClass: string = 'is-active';

	constructor() {
		this.toggleDropdownContent()
		this.onclickHandler()
		this.setHeaderValue()
	}

	private toggleDropdownContent = () => {
		this.dropdownItemArray.forEach((elem) => {
			elem.addEventListener("click", () => {
				if (!elem.parentElement.classList.contains(this.activeClass)) {
					// add active class to clicked, and remove from other
					this.removeActiveHelper();
					elem.parentElement.classList.add(this.activeClass);
				} else {
					// toggle active class on clicked elm
					elem.parentElement.classList.remove(this.activeClass);
				}
			});
		});
	}
	private removeActiveHelper = () => {
		// remove active class helper
		this.dropdownItemListArray.forEach((test) => {
			test.classList.remove(this.activeClass);
		});
	}
	private onclickHandler = () => {
		// Close dropdown, when child element is clicked
		this.dropdownItemContentArray.forEach((elem) => {
			elem.addEventListener("click", () => {
				elem.parentElement.classList.remove(this.activeClass);
			});
		});
	}
	// set header on click
	private setHeaderValue = () => {
		this.dropdownItemContentSetHeaderArray.forEach((elem) => {
			elem.addEventListener("click", () => {
				const clickedVal = elem.innerText;
				let getParent = elem.parentElement.parentElement.parentElement;
				let getParentHeader = getParent.firstElementChild.firstElementChild as HTMLElement;
				getParentHeader.innerText = clickedVal;
			});
		});
	}
}

JavaScript - ClickhandlerModule.ts

 
	
const selector = '[data-close-attr]';
const activeClass: string = 'is-active'

export class ClickHandler {
	constructor() {
		document.addEventListener('click', this.hideOnClickOutside)
	}
	private hideOnClickOutside = (e: Event) => {
		if (!$(e.target).closest(selector).length) {
			if ($(selector).is(':visible')) {
				$(selector).removeClass(activeClass);
			}
		}
	}
}