Footer covers my content when using w3.css - html

I am trying out w3.css for styling, along with knockout, and when I use a footer, it covers the content near the bottom of the page.
I have a button at the bottom of my content. When the page resizes or is small enough, the footer covers the button.
See codepen, or the code below
function setting(color) {
this.color = ko.observable(color);
this.colorClassName = ko.computed(function() {
return "w3-hover-" + this.color();
}, this);
}
function myInput() {
this.data = ko.observable("");
this.nameValid = ko.computed(function() {
return !(this.data() == null || this.data().length == 0);
}, this);
this.error = ko.computed(function() {
//if (this.data() == null || this.data().length == 0)
if (this.nameValid() == false) {
return "Enter name";
} else {
return "";
}
}, this);
this.display = ko.computed(function() {
if (this.nameValid() == false) {
return "block";
} else {
return "none";
}
}, this);
this.ageData = ko.observable();
this.ageValid = ko.computed(function() {
var age = this.ageData() + "";
var patt = new RegExp(/^[0-9]+$/g); /// ^-from start, $-to end, [0-9] - 0 to 9 numbers only
var res = patt.test(age);
if (isNaN(age) == true || res == false) {
return false;
} else {
return true;
}
}, this);
this.ageError = ko.computed(function() {
if (this.ageValid() == false) {
return "Enter a valid age";
} else {
return "";
}
}, this);
this.ageDisplay = ko.computed(function() {
if (this.ageValid() == true) {
return "none";
} else {
return "block";
}
}, this);
this.phone = ko.observable('http://digitalbush.com/projects/masked-input-plugin/');
this.allValid = ko.computed(function() {
return this.ageValid() && this.nameValid();
}, this);
}
function myModel() {
this.name = "Ice-Cream";
this.items = [{
name: "Chocolate",
price: 10
}, {
name: "Vanilla",
price: 12
}];
this.style = new setting('pale-green');
this.input = new myInput();
this.changeColor = function() {
if (this.style.color().indexOf('blue') == -1) {
this.style.color('pale-blue');
} else {
this.style.color('pale-green');
}
};
}
ko.applyBindings(new myModel());
<script type='text/javascript' src='http://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js'></script>
<link href="http://www.w3schools.com/lib/w3.css" rel="stylesheet" type="text/css">
<body class="w3-content w3-pale-blue" style="max-width:100%">
<header class="w3-container w3-pale-green w3-border">
<h1>Hello</h1>
</header>
<div class="w3-container w3-pale-yellow w3-border w3-padding-hor-16 w3-content" style="max-width:100%">
W3.CSS
<p>
The item is <span data-bind="text: name"></span> today.
<br />Available flavours are:
</p>
<div class="w3-container">
<ul data-bind="foreach: items" class="w3-ul w3-left w3-border w3-border-red">
<li class="w3-ul w3-hoverable w3-border-red " data-bind="css: $parent.style.colorClassName()">
<span data-bind="text: name"></span> is $<span data-bind="text:price" />
</li>
</ul>
</div>
<label class="w3-label w3-text-blue w3-xlarge">Name</label>
<input class="w3-input w3-border" type="text" data-bind="textInput: input.data">
<label class="w3-label w3-text-red w3-large" data-bind="text: input.error(), style: { display: input.display()}"></label>
<br />
<label class="w3-label w3-text-blue w3-xlarge">Age</label>
<input class="w3-input w3-border" type="text" data-bind="textInput: input.ageData">
<label class="w3-label w3-text-red w3-large" data-bind="text: input.ageError(), style: { display: input.ageDisplay()}"></label>
<br />
<label class="w3-label w3-text-blue w3-xlarge">Phone</label>
<input class="w3-input w3-border" type="text" data-bind="textInput: input.phone">
<!--<label class="w3-label w3-text-red w3-large" data-bind="text: input.phoneError(), style: { display: input.phoneDisplay()}"></label>-->
<br />
<button class="w3-btn w3-border w3-border-teal w3-round w3-teal" data-bind="click: changeColor, enable: input.allValid()">Test</button>
</div>
<footer class="w3-light-grey w3-bottom">
<div class="w3-container">
<p>This is my footer</p>
</div>
</footer>

My solution was to add another div element with the same content as my footer, but make it invisible. This way it will fill the space behind the real footer.
In the code i above i will add the following
<div class="w3-container" style="opacity:0">
<p>This is my footer</p>
</div>
The updated codepen shows the solution.

The elements .w3-top, .w3-bottom have a position of :fixed, so they're always going to stick to the page.
Remove this from their stylesheet or add an alternative to your own.
E.g.
.w3-bottom {
position: static;
}

The class of your footer is w3-bottom, and by defaut, its position is fixed, so you need to change it to relative:
.w3-bottom {
position: relative !important;
}

Another way could be to place the "button" in its own "div" to better control its attributes, remove the "div" from the footer and the bottom class. Something like:
<div class="w3-container w3-padding-bottom-32">
<button class="w3-btn w3-border w3-border-teal w3-round w3-teal" data-bind="click: changeColor, enable: input.allValid()">Test</button>
</div>
<footer class="w3-container w3-light-grey">
<p>This is my footer</p>
</footer>
Please let me know if it works for you.
Kindly, Edwin

Try to use the styles from Sticky footer example from the bootstrap.
But this method have one disadvantage: footer have fixed height :(
Example:
/* Sticky footer styles
-------------------------------------------------- */
html {
position: relative;
min-height: 100%;
}
body {
/* Margin bottom by footer height */
margin-bottom: 80px;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
overflow: hidden;
/* Set the fixed height of the footer here */
height: 80px;
background-color: #f5f5f5;
}
<!DOCTYPE html>
<html>
<title>W3.CSS</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://www.w3schools.com/lib/w3.css">
<body>
<div class="w3-container w3-teal">
<h1>Header</h1>
</div>
<div class="w3-container">
<p>The w3-container class can be used to display headers.</p>
</div>
<div class="footer w3-container w3-teal">
<h5>Footer</h5>
<p>Footer information goes here</p>
</div>
</body>
</html>

Related

Preview Multiple Images Vue JS with background image css style

I'm trying to display a preview of some images when a user selects them before he uploads them, but I'm new to vue and I can't figure out how to apply the background image url style to each div (it must be with background image), here is my UploadComponent so far:
<template>
<div class="content">
<h1>{{ title }}</h1>
<div class="btn-container">
<label class="button" for="images">Select Images</label>
<input type="file" id="images" name="images[]" multiple="multiple" #change="selectFiles"/>
</div>
<br><br>
<div class="block">
<h3>Selected images</h3>
<div v-for="image in images" v-bind:key="image" class="image-result" v-bind:style="{ backgroundImage: 'url(' + image + ')' }">
</div>
</div>
<div class="btn-container">
<button type="button" class="submit-btn" v-on:click="uploadImages">Submit Images</button>
</div>
<br><br>
<div class="block">
<h3>Uploaded images</h3>
<div class="files-preview">
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'UploadComponent',
data: () => ({
title: 'Select your images first and then, submit them.',
images: []
}),
methods:{
selectFiles(event){
var selectedFiles = event.target.files;
var i = 0;
for (i = 0; i < selectedFiles.length; i++){
this.images.push(selectedFiles[i]);
}
for (i = 0; i < this.images.length; i++){
let reader = new FileReader(); //instantiate a new file reader
reader.addEventListener('load', function(){
console.log(this);
}.bind(this), false); //add event listener
reader.readAsDataURL(this.images[i]);
}
},
uploadImages(){
const data = new FormData();
data.append('photos', this.images);
axios.post('url')
.then(res => {
console.log(res);
});
}
}
}
</script>
The divs are being created but I'm missing the part where I assign the background image, how can I solve this?
You can't just put a File directly as the parameter for background-image. You started using FileReader but didn't do anything with the result.
Here's what I would do below, I converted your images array into a list of objects that have a file property and a preview property, just to keep the file and preview linked together.
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: "#app",
data: () => {
return {
title: 'Select your images first and then, submit them.',
images: []
}
},
methods: {
selectFiles(event) {
var selectedFiles = event.target.files;
for (var i = 0; i < selectedFiles.length; i++) {
let img = {
file: selectedFiles[i],
preview: null
};
let reader = new FileReader();
reader.addEventListener('load', () => {
img.preview = reader.result;
this.images.push(img);
});
reader.readAsDataURL(selectedFiles[i]);
}
},
uploadImages() {
const data = new FormData();
data.append('photos', this.images.map(image => image.file));
axios.post('url')
.then(res => {
console.log(res);
});
}
}
});
.image-result {
width: 100px;
height: 100px;
background-size: contain;
background-repeat: no-repeat;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" class="content">
<h1>{{ title }}</h1>
<div class="btn-container">
<label class="button" for="images">Select Images</label>
<input type="file" id="images" name="images[]" multiple="multiple" #change="selectFiles" />
</div>
<br><br>
<div class="block">
<h3>Selected images</h3>
<div id="target-photos">
</div>
<div v-for="image in images" class="image-result" v-bind:style="{ backgroundImage: 'url(' + image.preview + ')' }">
</div>
</div>
<div class="btn-container">
<button type="button" class="submit-btn" v-on:click="uploadImages">Submit Images</button>
</div>
<br><br>
<div class="block">
<h3>Uploaded images</h3>
<div class="files-preview">
</div>
</div>
</div>

Material design for web - how to make an autocomplete input

I am using google's material design for web. And I would like to use their select component as an autocomplete component. What I am aiming for is the autocomplete you can see in react mui. I have removed the disabled and readonly input attributes but I still can't write anything to input field.
<div class="mdc-select demo-width-class">
<div class="mdc-select__anchor">
<span class="mdc-select__ripple"></span>
<input type="text" class="mdc-select__selected-text">
<i class="mdc-select__dropdown-icon"></i>
<span class="mdc-floating-label">Pick a Food Group</span>
<span class="mdc-line-ripple"></span>
</div>
<div class="mdc-select__menu mdc-menu mdc-menu-surface mdc-menu-surface--fullwidth">
<ul class="mdc-list">
<li class="mdc-list-item mdc-list-item--selected" data-value="" aria-selected="true"></li>
<li class="mdc-list-item" data-value="grains">
<span class="mdc-list-item__text">
Bread, Cereal, Rice, and Pasta
</span>
</li>
<li class="mdc-list-item" data-value="vegetables">
<span class="mdc-list-item__text">
Vegetables
</span>
</li>
<li class="mdc-list-item" data-value="fruit">
<span class="mdc-list-item__text">
Fruit
</span>
</li>
</ul>
</div>
</div>
How can I enable the input field for writing into it, so that I can adapt and make an autocomplete input field?
since you have mentioned that you would like to see an answer without using react, thus in plain javascript. I have managed to do just that. I have combined the css and markup from #Sifat Haque's answer and the full working autocomplete logic from w3schools. Though this may seem simple, it was quite a hassle to make this work.
const select = new mdc.select.MDCSelect(document.querySelector('.mdc-select'));
function autocomplete(inp, arr) {
var currentFocus;
inp.addEventListener("input", autocomp);
inp.addEventListener("click", autocomp);
inp.addEventListener("focus", autocomp);
function autocomp(e) {
var a, b, i, val = this.value;
closeAllLists();
currentFocus = -1;
a = document.createElement("ul");
a.setAttribute("id", this.id + "autocomplete-list");
a.setAttribute("class", "autocomplete-items mdc-list");
document.getElementById("autocomp").appendChild(a);
for (i = 0; i < arr.length; i++) {
if (arr[i].substr(0, val.length).toUpperCase() == val.toUpperCase() || (val.trim()).length == 0) {
b = document.createElement("li");
b.setAttribute("class", "mdc-list-item")
b.innerHTML = "<span class='mdc-list-item__text'>" + arr[i] + "</span>";
b.innerHTML += "<input type='hidden' value='" + arr[i] + "'>";
b.addEventListener("click", function(e) {
inp.value = this.getElementsByTagName("input")[0].value;
closeAllLists();
});
a.appendChild(b);
}
}
}
inp.addEventListener("keydown", function(e) {
var x = document.getElementById(this.id + "autocomplete-list");
if (x) x = x.getElementsByTagName("li");
if (e.keyCode == 40) {
currentFocus++;
addActive(x);
} else if (e.keyCode == 38) { //up
currentFocus--;
addActive(x);
} else if (e.keyCode == 13) {
e.preventDefault();
if (currentFocus > -1) {
if (x) x[currentFocus].click();
}
}
});
function addActive(x) {
if (!x) return false;
removeActive(x);
if (currentFocus >= x.length) currentFocus = 0;
if (currentFocus < 0) currentFocus = (x.length - 1);
x[currentFocus].classList.add("autocomplete-active");
x[currentFocus].classList.add("mdc-list-item--selected");
}
function removeActive(x) {
for (var i = 0; i < x.length; i++) {
x[i].classList.remove("autocomplete-active");
x[i].classList.remove("mdc-list-item--selected");
}
}
function closeAllLists(elmnt) {
var x = document.getElementsByClassName("autocomplete-items");
for (var i = 0; i < x.length; i++) {
if (elmnt != x[i] && elmnt != inp) {
x[i].parentNode.removeChild(x[i]);
}
}
}
}
/*An array containing all the foods :*/
var foods = ["fruit", "vegetables", "grains", "fries"];
/*initiate the autocomplete function on the "myInput" element, and pass along the foods array as possible autocomplete values:*/
autocomplete(document.getElementById("name-input"), foods);
function makeActive(element) {
document.getElementById("name-input").focus();
element.classList.add("mdc-select--focused");
element.classList.add("mdc-select--activated")
}
* {
box-sizing: border-box;
}
.autocomplete {
position: relative;
display: inline-block;
}
input {
border: 1px solid transparent;
background-color: #f1f1f1;
padding: 10px;
font-size: 16px;
}
input[type=text] {
background-color: transparent;
width: 100%;
margin-left: -200px;
margin-top: 30px;
z-index: -2;
}
input[type=text]:active {
border: none;
}
.autocomplete-items {
position: absolute;
border: 1px solid #d4d4d4;
border-bottom: none;
border-top: none;
z-index: 99;
/*position the autocomplete items to be the same width as the container:*/
top: 100%;
left: 0;
right: 0;
max-height: 200px;
/*overflow-y: scroll; */
}
.autocomplete-items li {
padding: 10px;
cursor: pointer;
background-color: #fff;
border-bottom: 1px solid #d4d4d4;
}
.mdc-select__menu {
margin-top: -30px;
z-index: 1;
height: 150px;
box-shadow: none;
background-color: transparent;
overflow-x: hidden !important;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<link href="https://unpkg.com/material-components-web#v4.0.0/dist/material-components-web.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<script src="https://unpkg.com/material-components-web#v4.0.0/dist/material-components-web.min.js"></script>
</head>
<body>
<h2>Autocomplete</h2>
<p>Start typing:</p>
<!--Make sure the form has the autocomplete function switched off:-->
<form autocomplete="off" action="" method="post">
<div class="mdc-select" onclick="makeActive(this)">
<div class="mdc-select__anchor demo-width-class">
<i class="mdc-select__dropdown-icon"></i>
<div class="mdc-select__selected-text"></div>
<span class="mdc-floating-label">Pick a Food Group</span>
<div class="mdc-line-ripple"></div>
<input type="text" id="name-input" name="selectione">
</div>
<div class="mdc-select__menu mdc-menu mdc-menu-surface">
<div class="autocomplete" id='autocomp' style="width:200px;">
</div>
</div>
</div>
<input type="submit">
</form>
Hope this helps!
edit: added standard selection option
I've created a demo and it somewhat does what you want. The two issues it has are-
User has to click twice(double click) to input values(I can't to make it work on a single click)
The position of the label overlaps the selected value(Even after giving another class or styling it dynamically)
If anybody has any idea about how to go through these issues, their ideas are
welcome. #Leff, you yourself look like a person with experience, can
you solve them? If yes, please do so and please also enlighten us(me).
Also, I've used jquery in this code if you're looking for only vanilla javascript, you might have to do it yourself or find a person who can as I'm no expert in that area but that's the logical part. Your main problem seemed to be of HTML which should be resolved.
Below is the demo, see if it helps you.
// initialize
const select = new mdc.select.MDCSelect(document.querySelector('.mdc-select'));
// stop the original propagation so that input field remains editable
$('#food').on('click', (event) => {
return false;
});
// Demo Data
const foodArr = ['Bread, Cereal, Rice, and Pasta', 'Vegetables', 'Fruit'];
// You'll have to use ajax here to get the data
$('#food').on('keyup', (event) => {
//console.clear();
let $this = $(event.currentTarget);
let currValue = $this.val();
let search = new RegExp(currValue, 'i'); // prepare a regex object // Your custom condition
let matchArr = foodArr.filter(item => search.test(item)); //store the result in an array
let $select = "";
// check if array is empty
if (matchArr.length > 0) {
// map the elements of the array and create option html
matchArr.forEach((item) => {
$select += `<li class="mdc-list-item" data-value="${item}"> ${item}</li>`;
})
} else { // if array is empty, display no match
$select += `<li class="mdc-list-item" id="no_match"> No match found!</li>`;
}
//console.log(matchArr);
// if the data was selected before, unselect it
$('.mdc-list-item--selected:first').attr({'data-value': ''});
$('.mdc-list-item--selected:first').text('');
$('.mdc-list-item--selected:not(:first)').removeClass('mdc-list-item--selected');
// remove all previous option elements
$('.mdc-list-item:not(.mdc-list-item--selected:first)').remove();
// add new option elements
$('.mdc-list-item--selected').after($select);
// start the click function, so that dropdown doesn't close
$this.click();
});
// When any option is selected, show it on input field
$(document).on('click', '.mdc-list-item:not(#no_match)', (event) => {
let $this = $(event.currentTarget);
$this.addClass('mdc-list-item--selected');
$('.mdc-floating-label').addClass('mdc-floating-label--float-above');
$('.mdc-select__anchor').addClass('demo-width-class mdc-ripple-upgraded')
$('.mdc-line-ripple').addClass('mdc-line-ripple--active mdc-line-ripple--deactivating')
$('.mdc-line-ripple').css({'transform-origin': '182px center'})
$('#food').val($this.attr('data-value'));
// return false;
// event.stopImmediatePropagation()
});
// if clicked on no match, value of input field should be empty, alternatively you can also make option disabled
$(document).on('click', '#no_match', (event) => {
$('#food').val('');
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>#sauhardnc</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="https://unpkg.com/material-components-web#v4.0.0/dist/material-components-web.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<script src="https://unpkg.com/material-components-web#v4.0.0/dist/material-components-web.min.js"></script>
</head>
<body>
<div class="mdc-select">
<div class="mdc-select__anchor demo-width-class" style="width: 100%">
<i class="mdc-select__dropdown-icon"></i>
<input type="text" class="mdc-select__selected-text" id="food"></input>
<!-- Give a unique id -->
<!--<div contenteditable="true" class="mdc-select__selected-text" id="fruit"></div>-->
<span class="mdc-floating-label">Pick a Food Group</span>
<div class="mdc-line-ripple"></div>
</div>
<div class="mdc-select__menu mdc-menu mdc-menu-surface demo-width-class" style="width: 100%">
<ul class="mdc-list">
<li class="mdc-list-item mdc-list-item--selected" data-value="" aria-selected="true"></li>
<li class="mdc-list-item" data-value="Bread, Cereal, Rice, and Pasta">
Bread, Cereal, Rice, and Pasta
</li>
<li class="mdc-list-item" data-value="Vegetables">
Vegetables
</li>
<li class="mdc-list-item" data-value="Fruit">
Fruit
</li>
</ul>
</div>
</div>
</body>
</html>
You need to combine the input fields along with the select to get an input field and then write some javascript to get the autocomplete functionality. You can check my solution.
console.clear();
const select = new mdc.select.MDCSelect(document.querySelector('.mdc-select'));
select.listen('MDCSelect:change', () => {
alert(`Selected option at index ${select.selectedIndex} with value "${select.value}"`);
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<link href="https://unpkg.com/material-components-web#v4.0.0/dist/material-components-web.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<script src="https://unpkg.com/material-components-web#v4.0.0/dist/material-components-web.min.js"></script>
</head>
<body>
<div class="mdc-select">
<div class="mdc-select__anchor demo-width-class">
<i class="mdc-select__dropdown-icon"></i>
<div class="mdc-select__selected-text"></div>
<span class="mdc-floating-label">Pick a Food Group</span>
<div class="mdc-line-ripple"></div>
</div>
<div class="mdc-select__menu mdc-menu mdc-menu-surface demo-width-class">
<ul class="mdc-list">
<input type="text" class="mdc-list-item--selected mdc-text-field__input" id="name-input">
<label for="name-input" class="mdc-floating-label">search....</label>
<li class="mdc-list-item" data-value="grains">
Bread, Cereal, Rice, and Pasta
</li>
<li class="mdc-list-item" data-value="vegetables">
Vegetables
</li>
<li class="mdc-list-item" data-value="fruit">
Fruit
</li>
<input type="hidden" name="input_name" value="input_value" class="my_mdc-select__value" />
</ul>
</div>
</div>
</body>
</html>
This is what I could able to achieve, hope this helps in your implementation. I have not completely implemented the filter logic and all but was able to enter input and display the dropdown simultaneously.
const menuElement = document.querySelector(".mdc-menu");
// const menu = new mdc.menu.MDCMenu(menuElement);
const inputLabel = document.querySelector(".mdc-text-field");
const inputElem = inputLabel.querySelector("input");
const dropdownIcon = document.querySelector(".mdc-select__dropdown-icon");
let isMenuOpen = false;
inputLabel.addEventListener("click", () => {
inputElem.focus();
// menu.open = true;
if (!isMenuOpen) {
menuElement.classList.remove("list-menu-close");
menuElement.classList.add("list-menu-open");
dropdownIcon.classList.add("dropdown-icon-up");
} else {
menuElement.classList.remove("list-menu-open");
menuElement.classList.add("list-menu-close");
dropdownIcon.classList.remove("dropdown-icon-up");
}
isMenuOpen = !isMenuOpen;
});
inputElem.addEventListener("blur", () => {
menuElement.classList.remove("list-menu-open");
menuElement.classList.add("list-menu-close");
dropdownIcon.classList.remove("dropdown-icon-up");
isMenuOpen = false;
});
<div class="dropdown-container">
<label class="mdc-text-field mdc-text-field--filled">
<span class="mdc-text-field__ripple"></span>
<input
class="mdc-text-field__input"
type="text"
aria-labelledby="my-label-id"
/>
<i class="mdc-select__dropdown-icon"></i>
</label>
<div>
<div class="mdc-menu mdc-menu-surface list-menu">
<ul
class="mdc-list"
role="menu"
aria-hidden="true"
aria-orientation="vertical"
tabindex="-1"
>
<li class="mdc-list-item" role="menuitem">
<span class="mdc-list-item__text">A Menu Item</span>
</li>
<li class="mdc-list-item" role="menuitem">
<span class="mdc-list-item__text">Another Menu Item</span>
</li>
</ul>
</div>
</div>
</div>
.dropdown-container {
position: relative;
}
.list-menu {
position: absolute;
top: 60px;
}
.list-menu-open {
display: block;
opacity: 1;
}
.list-menu-close {
display: none;
opacity: 0;
}
.dropdown-icon-up {
transform: rotate(180deg) translateY(-5px);
}
Based on the answers here, I created my own function. Also the Material Design for Web Versions in the answers are quite old. Hope this helps for those looking for something like this.
At first, instead of using MDCSelect, I used MDCTextField and MDCMenu components. I thought everything would be easier this way. It took some time though, but I'm done.
The function that triggers the Menu to open when clicking on the TextField may seem a bit weird. #Zachary Haber explains this,
It appears that the inconsistency is due to a race condition. Clicking
on the menu causes the focus to leave the input which causes the menu
to close. And the menu closing causes focus to move back to the input
making it open again.
The issue is that the menu often closes before the menu has a chance
to send out the selected event.
To review the original answer for MDCMenu click issue: https://stackoverflow.com/a/61965646/5698079
Here is a working example;
const textFields = document.querySelectorAll('.mdc-text-field');
textFields.forEach(field => {
mdc.textField.MDCTextField.attachTo(field);
});
const menuS = document.querySelector("#menu");
const menu = new mdc.menu.MDCMenu(menuS);
const searchFieldS = document.querySelector("#food");
menu.setAnchorCorner(mdc.menuSurface.Corner.BOTTOM_LEFT);
document.querySelectorAll('#menu li').forEach(function(li) {
li.addEventListener('click', function() {
const selectedLi = this.getAttribute("data-value");
if (selectedLi != "notfound") {
// If you are going to post the text field data, I recommend you to get data-value.
searchFieldS.value = selectedLi;
searchFieldS.setAttribute("data-value", selectedLi);
}
});
});
// Open the menu when text field is focused.
(function() {
let menuFocused = false;
searchFieldS.addEventListener("focusin", () => {
if (!menuFocused) menu.open = true;
});
searchFieldS.addEventListener("click", () => {
menu.open = true;
});
menuS.addEventListener("focusin", () => {
menuFocused = true;
});
menuS.addEventListener("focusout", () => {
// This interval is to help make sure that input.focusIn doesn't re-open the menu
setTimeout(() => {
menuFocused = false;
}, 0);
});
searchFieldS.addEventListener("focusout", () => {
setTimeout(() => {
if (!menuFocused) menu.open = false;
}, 0);
});
})();
searchFieldS.addEventListener("keyup", function(e) {
const keyT = event.target.value.toUpperCase();
const menuList = document.querySelectorAll('.mdc-deprecated-list > li > .mdc-deprecated-list-item__text');
const menuLiss = document.querySelectorAll('.mdc-deprecated-list-item');
const noDataEl = document.querySelector("#menuNoData");
//
const arr = [];
menuList.forEach(function(searchItem) {
if (searchItem.parentElement.getAttribute('id') != "menuNoData") {
searchItem.parentElement.dataset.isfound = searchItem.textContent.toUpperCase().includes(keyT) ? "true" : "false";
arr.push(searchItem.parentElement.getAttribute("data-isfound"));
}
});
if (arr.filter(function(countArr) {
return countArr == "true"
}).length == 0) {
noDataEl.classList.remove("hide-none");
} else {
if (!noDataEl.classList.contains("hide-none")) {
noDataEl.classList.add("hide-none");
}
}
});
li[data-isfound="false"] {
display: none;
}
.hide-none {
display: none !important;
}
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<script src="https://unpkg.com/material-components-web#latest/dist/material-components-web.min.js"></script>
<link href="https://unpkg.com/material-components-web#latest/dist/material-components-web.min.css" rel="stylesheet">
<div class="mdc-menu-surface--anchor">
<label class="mdc-text-field mdc-text-field--outlined">
<span class="mdc-notched-outline">
<span class="mdc-notched-outline__leading"></span>
<span class="mdc-notched-outline__notch">
<span class="mdc-floating-label" id="my-label-id">Select a fruit..</span>
</span>
<span class="mdc-notched-outline__trailing"></span>
</span>
<input id="food" type="text" class="mdc-text-field__input" aria-labelledby="my-label-id">
</label>
<div id="menu" class="mdc-menu mdc-menu-surface mdc-menu-surface--fullwidth">
<ul class="mdc-deprecated-list" role="listbox" aria-label="Food picker listbox">
<li id="menuNoData" class="mdc-deprecated-list-item mdc-deprecated-list-item--disabled hide-none" aria-selected="false" aria-disabled="true" data-value="notfound" role="option" value="" data-value="">
<span class="mdc-deprecated-list-item__ripple"></span>
<span class="mdc-deprecated-list-item__text">No data found.</span>
</li>
<li class="mdc-deprecated-list-item" aria-selected="false" data-value="grains" role="option">
<span class="mdc-deprecated-list-item__ripple"></span>
<span class="mdc-deprecated-list-item__text">Bread, Cereal, Rice, and Pasta</span>
</li>
<li class="mdc-deprecated-list-item" aria-selected="false" data-value="vegetables" role="option">
<span class="mdc-deprecated-list-item__ripple"></span>
<span class="mdc-deprecated-list-item__text">Vegetables</span>
</li>
<li class="mdc-deprecated-list-item" aria-selected="false" data-value="fruit" role="option">
<span class="mdc-deprecated-list-item__ripple"></span>
<span class="mdc-deprecated-list-item__text">Fruit</span>
</li>
</ul>
</div>
</div>
CodePen: https://codepen.io/lastofdead/pen/bGMZPzO

Regex to validate a date

so basically, I am creating a booking form for a practice website. I want to validate the date so that the user can only enter a date in 2017, and cannot proceed without entering a date in 2017. They also cannot proceed without entering a valid name and email. An alert message appears if one of these is not satisfied, and the related text boxes are highlighted.
Here is what I have so far in terms of code:
Any help is appreciated, thank you.
HTML
<html>
<head>
<title> Booking </title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="styles.css">
<script>
function validate{} {
result = true;
contentUsername=booking.username.value;
if (contentUsername=="")
result=false;
}
</script>
<script>
function validate() {
result = true;
contentUsername=booking.username.value;
contentEmail=booking.email.value;
contentDate=booking.date.value;
var email = /^[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$/;
var username = /^[A-Za-z]+[A-Za-z\s\.'-]+[A-Za-z]$/;
var date = /^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[1,3-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d).\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d).(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d).\d{2})$/;
var alertMessage="";
if (contentUsername==""){
result=false;
document.getElementById('username').style.background="lightyellow";
document.getElementById('username').style.border="solid firebrick 1px";
}
if (contentEmail=="") {
result=false;
document.getElementById('email').style.background="lightyellow";
document.getElementById('email').style.border="solid firebrick 1px";
}
if (contentDate=="") {
result=false;
document.getElementById('date').style.background"lightyellow";
document.getElementById('date').style.border="solid firebrick 1px";
}
if (!(email.test(contact.email.value)) && contact.email.value != "") {
alertMessage += contact.email.value + ' is not a valid email address.\r\n';
result=false;
}
if (!(username.test(contact.username.value)) && contact.username.value != "") {
alertMessage += contact.username.value + ' is not a valid name.\r\n';
result=false;
}
if (!(date.test(contact.date.value)) && contact.date.value != "") {
alertMessage += contact.date.value + ' is not a valid date. Please select a date in 2017.\r\n';
result=false;
}
if (!result) {
alertMessage += "Please fill out the highlighted fields";
alert(alertMessage);
}
return result;
}
</script>
</head>
<body>
<div id="header">
<div id="branding">
<img src="Images/logo.png">
</div><!-- end of "logo" -->
<div id="tagline">
<p> Welcome to yourday.com - We hope you enjoy your visit!
<br> This is where you can book an appointment with one of our agents!
<br> Please note: Dates for 2016 are fully booked. Next available appointments are in 2017. We apologies for any inconvenience caused.</p>
</div><!-- end of "tagline" -->
</div><!-- end of "header" -->
<div id="bodycontent4">
<form action="http://www.rebol.com/cgi-bin/test-cgi.cgi" method="post" class="booking" id="booking" onsubmit="return validate()">
<fieldset>
<legend>Booking</legend>
<label for="username">Name: </label>
<input type="text" name="username" id="username"></br>
<label for="email">E-mail:</label>
<input type="text" name="email" id="email"></br>
<label for="date">Date:</label>
<input type="date" name="date" id="date"></br>
<label for="location">Location:</label>
<select>
<option value="manor">Uppercourt Manor</option>
<option value="killruddery">Killrudderry</option>
<option value="carriage">The Carriage Rooms</option>
<option value="coolclogher">Coolclogher House</option>
</select>
</fieldset>
<input type="submit" value="Submit">
</form>
</div><!--end of "bodycontent" -->
<div id="navigation">
<ul class="topnav" id="TopNav">
<li>Home</li>
<li>Locations</li>
<li>Booking</li>
<li>Testimonials</li>
<li>Contact Us</li>
<li>About</li>
</li>
</ul>
</div> <!--end of "navigation" -->
<div id="footer" style = "position: absolute; top: 550px;">
<p>Created by: Calvin Leong</p>
<p>Contact information: calvin.leong#CLDesign.com</p>
</div>
</body>
</html>
CSS
/* Booking Form */
form.booking label {
display: block;
width: 100px;
float: left;
font-weight: bold;
font-size: small;
color: black;
line-height: 150%
}
form.booking fieldset {
border: 2px solid red;
padding: 10px;
}
form.booking legend {
font-weight: bold;
font-size: small;
color: black;
padding: 5px;
}
#bodycontent4 {
position: absolute;
top: 270px;
width: 25%;
left: 500px;
}
#div {
margin: 0 auto;
}
You could do like this:
var date = new Date("01/01/2017");
var year = date.getFullYear();
if(year == 2017){
alert(year);
}else{
alert("crap");
}
Here is your regex for date:
var date = /^2017-\d{2}-\d{2}$/;
And, here is the working link: http://jsbin.com/getunisagi

manually replicate bootstraps typehead-show-hint with angular, css, html

Is there a away to replicate bootstraps typehead suggestive hint feature only using angular, html and css. I would like to show the first item of the search results dynamically in the search box!
<body>
<div ng-app="demoApp" ng-controller='demoCtrl'>
<br>
<input type="text" ng-model="search_term" placeholder="search...">
<br>
<div ng-repeat="(index, item) in items | filter:search_term" ng-class="{'is-first' : index == 0}">{{item}}</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<script>
var app = angular.module('demoApp', []);
app.controller('demoCtrl', ['$scope', function($scope) {
$scope.items = [
'Apple',
'Apricot',
'Avocado',
'Banana',
'Bilberry',
'Blackberry',
'Blackcurrant',
'Blueberry',
'Boysenberry',
'Cantaloupe',
'Currant',
'Cherry',
'Cherimoya',
'Cloudberry',
'Coconut',
'Cranberry',
'Damson',
'Date',
'Dragonfruit'
];
}]);
</script>
<style>
/* put any CSS you need here */
.is-first {
color:red;
}
</style>
</body>
Any help appreciated!
My solution in the end, I used ng-change to hide/show a duplicate input box which returned the first item on the search filter:
<body>
<div ng-app="demoApp" ng-controller='demoCtrl'>
<br>
<input class="main" type="text" ng-model="search_term" placeholder="search..." ng-change="showSuggest()">
<br>
<div ng-show="suggestion" class="autocomplete" ng-repeat="(index, item) in items | filter:search_term | limitTo:1">
<input disabled type="text" placeholder=" {{ item }}">
</div>
<div ng-repeat="(index, item) in items | filter:search_term" ng-class="{'is-first' : index == 0}">{{item}}</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<script>
var app = angular.module('demoApp', []);
app.controller('demoCtrl', ['$scope', function($scope) {
$scope.suggestion = false;
$scope.items = [
'Apple',
'Apricot',
'Avocado',
'Banana',
'Bilberry',
'Blackberry',
'Blackcurrant',
'Blueberry',
'Boysenberry',
'Cantaloupe',
'Currant',
'Cherry',
'Cherimoya',
'Cloudberry',
'Coconut',
'Cranberry',
'Damson',
'Date',
'Dragonfruit'
];
$scope.showSuggest = function(){
if ($scope.search_term.length < 1)
{$scope.suggestion = false}
else {$scope.suggestion = true}
};
}]);
</script>
<style>
/* put any CSS you need here */
.is-first {
color:red;
}
.main{
position: absolute;
top: 10;
left: 10;
z-index: 11;
background: transparent;
}
.autocomplete{
position: absolute;
top: 10;
left: 10;
background: transparent;
z-index: 10;
}
</style>

Links are not clickable?

As I'm designing my node.js application, I'm having trouble figuring out why my anchor links are not working/not clickable. The code itself was working fine before I designed the app. Is there a CSS property I need to configure to fix this? This is for the .player ul li links.
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<link href='http://fonts.googleapis.com/css?family=Bree+Serif' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="/style.css">
<link href='http://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Indie+Flower' rel='stylesheet' type='text/css'>
<script src="http://connect.soundcloud.com/sdk.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<!-- <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css"> -->
<meta charset="UTF-8">
<title>Open Mic Chatroom</title>
</head>
<header>
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<!-- <a class="navbar-brand" href="#">
<img alt="Brand" src="...">
</a> -->
<h1>OpenMic</h1>
</div>
</div>
</nav>
</header>
<body>
<div class="container">
<div class="row">
<div class="musicians col-sm-3 col-md-3">
<!-- <div class="musicians col-sm-6 col-md-4"> -->
<!-- <div class="col-sm-6 col-md-4"> -->
<!-- <div class="thumbnail musicians-box"> -->
<!-- <img src="..." alt="..."> -->
<div class="caption">
<h2>Musicians Online</h2>
<p>
<div id="chatters">
</div>
</p>
<!-- <p>Button Button</p> -->
<!-- </div> -->
</div>
</div>
<div class="chatroom col-sm-8 col-md-8">
<!-- <div class="chatroom col-sm-6 col-md-4">
--><!-- <div class="col-sm-6 col-md-4">-->
<!-- <div class="thumbnail chatroom-box"> -->
<!-- <img src="..." alt="..."> -->
<div class="caption">
<h2>Messages</h2>
<div id="messagesContainer"></div>
<!-- </div> -->
</div>
<form action="" id="chat_form">
<input id="info" autocomplete="off" />
<input type="submit" value="Submit">
</form>
</div>
</div>
</div>
<!-- <h3>Messages</h3>
<div id="messagesContainer"></div>
<form action="" id="chat_form">
<input id="info" autocomplete="off" />
<input type="submit" value="Submit">
</form> -->
<!-- SoundCloud Recording Feature -->
<div class="row player">
<div class="soundcloud-player">
<ul>
<div id="player">
<!-- <button type="button" id="startRecording" class="btn btn-default btn-lg">
<span class="glyphicon glyphicon-record" aria-hidden="true"></span>
</button> -->
<li id="startRecording">Start Recording</li>
</div>
<!-- <button type="button" id="stopRecording" class="btn btn-default btn-lg">
<span class="glyphicon glyphicon-stop" aria-hidden="true"></span>
</button> -->
<li id="stopRecording">Stop Recording</li>
<!-- <button type="button" id="playBack" class="btn btn-default btn-lg">
<span class="glyphicon glyphicon-play" aria-hidden="true"></span>
</button> -->
<li id="playBack">Play Recorded Sound</li>
<!-- <button type="button" id="upload" class="btn btn-default btn-lg">
<span class="glyphicon glyphicon-open" aria-hidden="true"></span>
</button> -->
<li id="upload">Upload</li>
</ul>
<p class="status"></p>
<div class="audioplayer">
</div>
</div>
</div>
<!-- <script src="/socket.io/socket.io.js"></script> -->
</div>
<script>
$(document).ready(function() {
var trackUrl;
//establish connection
var socket = io.connect('http://localhost:3000');
$('#chat_form').on('submit', function (e) {
e.preventDefault();
var message = $('#info').val();
socket.emit('messages', message);
$('#info').val('');
});
socket.on('messages', function (data) {
console.log("new message received");
$('#messagesContainer').append(data);
$('#messagesContainer').append('<br>');
// insertMessage(data);
});
socket.on('add_chatter', function (name) {
var chatter = $('<li>' + name + '</li>').data('name', name);
$('#chatters').append(chatter);
});
// //Embed SoundCloud player in the chat
// socket.on('track', function (track) {
// console.log("new track", track);
// SC.oEmbed(track, {auto_play: true}, function (oEmbed) {
// console.log('oEmbed response: ' + oEmbed);
// });
// SC.stream(track, function (sound) {
// console.log("streaming", sound);
// sound.play();
// });
// // socket.on('remove chatter', function (name) {
// // $('#chatters li[data-name=]' + name + ']').remove();
// });
//SOUNDCLOUD RECORDING FEATURE
function updateTimer(ms) {
// update the timer text. Used when user is recording
$('.status').text(SC.Helper.millisecondsToHMS(ms));
}
//Connecting with SoundCloud
console.log("calling SoundCloud initialize");
SC.initialize({
client_id: "976d99c7318c9b11fdbb3f9968d79430",
redirect_uri: "http://localhost:3000/auth/soundcloud/callback"
});
SC.connect(function() {
SC.record({
start: function() {
window.setTimeout(function() {
SC.recordStop();
SC.recordUpload({
track: { title: 'This is my sound' }
});
}, 5000);
}
});
//Adds SoundCloud username to chat app
console.log("connected to SoundCloud");
SC.get('/me', function(me) {
console.log("me", me);
socket.emit('join', me.username);
});
// SC.get('/me/tracks', {}, function(tracks) {
// var myTracks = $("<div>");
// var heading = $("<h1>").text("Your tracks");
// var list = $("<ul>");
// tracks.forEach(function (single) {
// var listItem = $("<li>").text(single.permalink);
// listItem.on("click", function () {
// SC.oEmbed(single.permalink_url, {
// auto_play: true
// }, function(oEmbed) {
// console.log("oEmbed", oEmbed);
// $('.status').html(oEmbed.html);
// });
// });
// list.append(listItem);
// });
// $("body").append(myTracks.append(heading, list));
// });
// username = prompt("What is your username?");
// socket.emit('join', username);
// });
//Start recording link
$('#startRecording a').click(function (e) {
$('#startRecording').hide();
$('#stopRecording').show();
e.preventDefault();
SC.record({
progress: function(ms, avgPeak) {
updateTimer(ms);
}
});
});
//Stop Recording link
$('#stopRecording a').click(function (e) {
e.preventDefault();
$('#stopRecording').hide();
$('#playBack').show();
$('upload').show();
SC.recordStop();
});
//Playback recording link
$('#playBack a').click(function (e) {
e.preventDefault();
updateTimer(0);
SC.recordPlay({
progress: function (ms) {
updateTimer(ms);
}
});
});
//Uploaded SoundCloud Track after recorded
$('#upload').click(function (e) {
e.preventDefault();
SC.connect({
connected: function() {
console.log("CONNECTED");
$('.status').html("Uploading...");
SC.recordUpload({
track: {
title: 'My Recording',
sharing: 'public'
}
},
//When uploaded track, provides link user, but not everyone
function (track) {
// console.log("TRACK", track);
setTimeout(function () {
SC.oEmbed(track.permalink_url, {auto_play: true}, function(oEmbed) {
console.log("THIS IS oEMBED", oEmbed);
// console.log(oEmbed.html);
socket.emit('messages', oEmbed.html );
$('.status').html(oEmbed.html);
});
}, 8000);
// $('.status').html("Uploaded: <a href='" + track.permalink_url + "'>" + track.permalink_url + "</a>");
});
}
});
});
});
});
</script>
</body>
</html>
CSS:
body {
background: url('microphone.svg') no-repeat center center fixed;
background-position: 4% 100%;
}
h1, h2, h3 {
font-family: 'Bree Serif', serif;
}
body {
font-family: 'Open Sans', sans-serif;
}
h1 {
color: white;
padding-left: 5%;
padding-bottom: 5%;
letter-spacing: 4px;
}
.navbar-default {
background-color: #000000;
/* background-image: url('SoundWave.jpg');
background-position: center;
opacity: 0.8;*/
}
input, button, select, textarea {
line-height: 3;
}
.chatroom, .musicians {
border-style: solid;
border-radius: 6%;
border-color: black;
margin-left: 20px;
height: 500px;
background-color: #F2F2F2;
opacity: 0.8;
}
.musicians {
height: fixed;
}
input#info {
margin: 47% 1%;
width: 656px;
border-radius: 10%;
}
.visualSoundContainer__teaser {
height: 100px;
}
.row {
padding-top: 2%;
}
.player {
text-align: center;
}
.player ul li {
z-index: 30;
}
Comment the margin property. It is cover the section. you can check it on firebug while selecting the anchor element.
input#info {
/*margin: 47% 1%;*/
width: 656px;
border-radius: 10%;
}
Working link: http://jsfiddle.net/95yz0h63/
add css
a{
cursor:pointer;
}