0
# Form Components
1
2
Enhanced form controls and input components including text fields, selects, date/time pickers, autocomplete, file inputs, and form validation with Material Design styling and JavaScript functionality.
3
4
## Capabilities
5
6
### Text Fields and Textarea
7
8
Enhanced text inputs with floating labels, validation, and character counting.
9
10
```javascript { .api }
11
/**
12
* Global form utilities available on M object
13
*/
14
declare const M: {
15
/** Update all text field labels (call after dynamic content changes) */
16
updateTextFields(): void;
17
18
/** Validate individual form field */
19
validate_field(field: Element): boolean;
20
21
/** Auto-resize textarea to fit content */
22
textareaAutoResize(textarea: Element): void;
23
};
24
```
25
26
**HTML Structure and CSS Classes:**
27
28
```html
29
<!-- Basic text input -->
30
<div class="input-field">
31
<input id="first_name" type="text" class="validate">
32
<label for="first_name">First Name</label>
33
</div>
34
35
<!-- Textarea with character counter -->
36
<div class="input-field">
37
<textarea id="textarea1" class="materialize-textarea" data-length="120"></textarea>
38
<label for="textarea1">Message</label>
39
</div>
40
41
<!-- Input with icon -->
42
<div class="input-field">
43
<i class="material-icons prefix">account_circle</i>
44
<input id="icon_prefix" type="text" class="validate">
45
<label for="icon_prefix">Username</label>
46
</div>
47
48
<!-- Input with validation -->
49
<div class="input-field">
50
<input id="email" type="email" class="validate">
51
<label for="email">Email</label>
52
<span class="helper-text" data-error="Invalid email" data-success="Valid email">Enter your email</span>
53
</div>
54
```
55
56
### Character Counter
57
58
Automatic character counting for text inputs and textareas.
59
60
```javascript { .api }
61
/**
62
* Character counter component
63
* @param el - Input or textarea element with data-length attribute
64
* @param options - Configuration options (currently empty)
65
*/
66
class CharacterCounter {
67
constructor(el: Element, options?: {});
68
69
static init(els: Element | NodeList, options?: {}): CharacterCounter | CharacterCounter[];
70
static getInstance(el: Element): CharacterCounter;
71
static get defaults(): {};
72
73
/** Update character count display */
74
updateCounter(): void;
75
76
destroy(): void;
77
}
78
```
79
80
**Usage Examples:**
81
82
```html
83
<!-- Character counter via data attribute -->
84
<div class="input-field">
85
<input id="input_text" type="text" data-length="10">
86
<label for="input_text">Input text</label>
87
</div>
88
89
<div class="input-field">
90
<textarea id="textarea2" class="materialize-textarea" data-length="120"></textarea>
91
<label for="textarea2">Textarea</label>
92
</div>
93
```
94
95
```javascript
96
// Initialize character counters
97
const elems = document.querySelectorAll('input[data-length], textarea[data-length]');
98
M.CharacterCounter.init(elems);
99
100
// Manual update after programmatic changes
101
const instance = M.CharacterCounter.getInstance(document.getElementById('input_text'));
102
instance.updateCounter();
103
```
104
105
### Select Dropdown
106
107
Enhanced select dropdowns with search functionality and custom styling.
108
109
```javascript { .api }
110
/**
111
* Form select component (enhanced dropdown select)
112
* @param el - Select element
113
* @param options - Configuration options
114
*/
115
class FormSelect {
116
constructor(el: Element, options?: FormSelectOptions);
117
118
static init(els: Element | NodeList, options?: FormSelectOptions): FormSelect | FormSelect[];
119
static getInstance(el: Element): FormSelect;
120
static get defaults(): FormSelectOptions;
121
122
/** Get array of selected values */
123
getSelectedValues(): string[];
124
125
destroy(): void;
126
}
127
128
interface FormSelectOptions {
129
/** Additional CSS classes for dropdown */
130
classes?: string;
131
132
/** Options to pass to underlying Dropdown component */
133
dropdownOptions?: {
134
alignment?: 'left' | 'right';
135
autoFocus?: boolean;
136
constrainWidth?: boolean;
137
container?: Element;
138
coverTrigger?: boolean;
139
closeOnClick?: boolean;
140
hover?: boolean;
141
inDuration?: number;
142
outDuration?: number;
143
};
144
}
145
```
146
147
**Usage Examples:**
148
149
```html
150
<!-- Basic select -->
151
<div class="input-field">
152
<select>
153
<option value="" disabled selected>Choose your option</option>
154
<option value="1">Option 1</option>
155
<option value="2">Option 2</option>
156
<option value="3">Option 3</option>
157
</select>
158
<label>Materialize Select</label>
159
</div>
160
161
<!-- Multiple select -->
162
<div class="input-field">
163
<select multiple>
164
<option value="" disabled selected>Choose your options</option>
165
<option value="1">Option 1</option>
166
<option value="2">Option 2</option>
167
<option value="3">Option 3</option>
168
</select>
169
<label>Multiple Select</label>
170
</div>
171
172
<!-- Select with icons -->
173
<div class="input-field">
174
<select>
175
<option value="" disabled selected>Choose your option</option>
176
<option value="1" data-icon="images/sample-1.jpg" class="left circle">Option 1</option>
177
<option value="2" data-icon="images/sample-2.jpg" class="left circle">Option 2</option>
178
</select>
179
<label>Select with Icons</label>
180
</div>
181
```
182
183
```javascript
184
// Initialize select dropdowns
185
const elems = document.querySelectorAll('select');
186
const instances = M.FormSelect.init(elems);
187
188
// Get selected values
189
const instance = M.FormSelect.getInstance(document.querySelector('select'));
190
const selectedValues = instance.getSelectedValues();
191
console.log('Selected:', selectedValues);
192
```
193
194
### Datepicker
195
196
Date selection component with calendar interface and internationalization support.
197
198
```javascript { .api }
199
/**
200
* Datepicker component
201
* @param el - Input element for date selection
202
* @param options - Configuration options
203
*/
204
class Datepicker {
205
constructor(el: Element, options?: DatepickerOptions);
206
207
static init(els: Element | NodeList, options?: DatepickerOptions): Datepicker | Datepicker[];
208
static getInstance(el: Element): Datepicker;
209
static get defaults(): DatepickerOptions;
210
211
/** Open datepicker */
212
open(): void;
213
214
/** Close datepicker */
215
close(): void;
216
217
/** Set selected date programmatically */
218
setDate(date?: Date): void;
219
220
/** Update input value from selected date */
221
setInputValue(): void;
222
223
/** Navigate to specific date */
224
gotoDate(date: Date): void;
225
226
destroy(): void;
227
}
228
229
interface DatepickerOptions {
230
/** Automatically close picker when date is selected */
231
autoClose?: boolean; // default: false
232
233
/** Date format for display */
234
format?: string; // default: 'mmm dd, yyyy'
235
236
/** Custom parse function for date strings */
237
parse?: (value: string, format: string) => Date;
238
239
/** Default date to show */
240
defaultDate?: Date;
241
242
/** Set default date as selected */
243
setDefaultDate?: boolean; // default: false
244
245
/** Disable weekends */
246
disableWeekends?: boolean; // default: false
247
248
/** Custom function to disable specific dates */
249
disableDayFn?: (date: Date) => boolean;
250
251
/** First day of week (0 = Sunday) */
252
firstDay?: number; // default: 0
253
254
/** Minimum selectable date */
255
minDate?: Date;
256
257
/** Maximum selectable date */
258
maxDate?: Date;
259
260
/** Year range for year selector */
261
yearRange?: number; // default: 10
262
263
/** Show clear button */
264
showClearBtn?: boolean; // default: false
265
266
/** Show month after year in title */
267
showMonthAfterYear?: boolean; // default: false
268
269
/** Show days of adjacent months */
270
showDaysInNextAndPreviousMonths?: boolean; // default: false
271
272
/** Container for datepicker modal */
273
container?: Element;
274
275
/** Internationalization options */
276
i18n?: {
277
cancel?: string;
278
clear?: string;
279
done?: string;
280
previousMonth?: string;
281
nextMonth?: string;
282
months?: string[];
283
monthsShort?: string[];
284
weekdays?: string[];
285
weekdaysShort?: string[];
286
weekdaysAbbrev?: string[];
287
};
288
289
/** Callbacks */
290
onSelect?: (date: Date) => void;
291
onOpen?: () => void;
292
onClose?: () => void;
293
onDraw?: () => void;
294
}
295
```
296
297
**Usage Examples:**
298
299
```html
300
<!-- Basic datepicker -->
301
<div class="input-field">
302
<input type="text" class="datepicker">
303
<label for="birthdate">Birthdate</label>
304
</div>
305
```
306
307
```javascript
308
// Initialize datepickers
309
const elems = document.querySelectorAll('.datepicker');
310
const instances = M.Datepicker.init(elems, {
311
format: 'yyyy-mm-dd',
312
autoClose: true,
313
minDate: new Date(2020, 0, 1), // January 1, 2020
314
maxDate: new Date(2025, 11, 31), // December 31, 2025
315
onSelect: (date) => console.log('Date selected:', date)
316
});
317
318
// Programmatic date setting
319
const instance = M.Datepicker.getInstance(document.querySelector('.datepicker'));
320
instance.setDate(new Date(2023, 5, 15)); // June 15, 2023
321
```
322
323
### Timepicker
324
325
Time selection component with clock interface and customizable options.
326
327
```javascript { .api }
328
/**
329
* Timepicker component
330
* @param el - Input element for time selection
331
* @param options - Configuration options
332
*/
333
class Timepicker {
334
constructor(el: Element, options?: TimepickerOptions);
335
336
static init(els: Element | NodeList, options?: TimepickerOptions): Timepicker | Timepicker[];
337
static getInstance(el: Element): Timepicker;
338
static get defaults(): TimepickerOptions;
339
340
/** Open timepicker */
341
open(): void;
342
343
/** Close timepicker */
344
close(): void;
345
346
destroy(): void;
347
}
348
349
interface TimepickerOptions {
350
/** Animation duration */
351
duration?: number; // default: 350
352
353
/** Container selector */
354
container?: string; // default: 'body'
355
356
/** Show clear button */
357
showClearBtn?: boolean; // default: false
358
359
/** Default time */
360
defaultTime?: string; // default: 'now'
361
362
/** Minutes from now as default time */
363
fromNow?: number; // default: 0
364
365
/** Internationalization */
366
i18n?: {
367
cancel?: string;
368
clear?: string;
369
done?: string;
370
};
371
372
/** Auto close on time selection */
373
autoClose?: boolean; // default: false
374
375
/** Use 12-hour format */
376
twelveHour?: boolean; // default: true
377
378
/** Vibrate on touch (mobile) */
379
vibrate?: boolean; // default: true
380
381
/** Callbacks */
382
onOpenStart?: () => void;
383
onOpenEnd?: () => void;
384
onCloseStart?: () => void;
385
onCloseEnd?: () => void;
386
onSelect?: (hour: number, minute: number) => void;
387
}
388
```
389
390
**Usage Examples:**
391
392
```html
393
<!-- Basic timepicker -->
394
<div class="input-field">
395
<input type="text" class="timepicker">
396
<label for="appt-time">Appointment Time</label>
397
</div>
398
```
399
400
```javascript
401
// Initialize timepickers
402
const elems = document.querySelectorAll('.timepicker');
403
const instances = M.Timepicker.init(elems, {
404
twelveHour: false, // 24-hour format
405
autoClose: true,
406
onSelect: (hour, minute) => console.log(`Time: ${hour}:${minute}`)
407
});
408
```
409
410
### Autocomplete
411
412
Autocomplete input with customizable data sources and filtering.
413
414
```javascript { .api }
415
/**
416
* Autocomplete component
417
* @param el - Input element
418
* @param options - Configuration options
419
*/
420
class Autocomplete {
421
constructor(el: Element, options?: AutocompleteOptions);
422
423
static init(els: Element | NodeList, options?: AutocompleteOptions): Autocomplete | Autocomplete[];
424
static getInstance(el: Element): Autocomplete;
425
static get defaults(): AutocompleteOptions;
426
427
/** Select option programmatically */
428
selectOption(el: Element): void;
429
430
/** Open autocomplete dropdown */
431
open(): void;
432
433
/** Close autocomplete dropdown */
434
close(): void;
435
436
/** Update autocomplete data */
437
updateData(data: { [key: string]: string | null }): void;
438
439
destroy(): void;
440
}
441
442
interface AutocompleteOptions {
443
/** Data object for autocomplete */
444
data?: { [key: string]: string | null }; // key: display text, value: image URL or null
445
446
/** Limit number of results */
447
limit?: number; // default: Infinity
448
449
/** Callback when autocomplete selection is made */
450
onAutocomplete?: (text: string) => void;
451
452
/** Minimum characters before autocomplete activates */
453
minLength?: number; // default: 1
454
455
/** Custom sort function for results */
456
sortFunction?: (a: string, b: string, inputString: string) => number;
457
}
458
```
459
460
**Usage Examples:**
461
462
```html
463
<!-- Autocomplete input -->
464
<div class="input-field">
465
<input type="text" id="autocomplete-input" class="autocomplete">
466
<label for="autocomplete-input">Autocomplete</label>
467
</div>
468
```
469
470
```javascript
471
// Initialize autocomplete
472
const elems = document.querySelectorAll('.autocomplete');
473
const instances = M.Autocomplete.init(elems, {
474
data: {
475
"Apple": null,
476
"Microsoft": null,
477
"Google": "https://placehold.it/250x250",
478
"Amazon": null,
479
"Facebook": null
480
},
481
limit: 5,
482
onAutocomplete: (val) => console.log('Autocomplete value:', val)
483
});
484
485
// Update data dynamically
486
const instance = M.Autocomplete.getInstance(document.getElementById('autocomplete-input'));
487
instance.updateData({
488
"New Item 1": null,
489
"New Item 2": "https://example.com/image.jpg"
490
});
491
```
492
493
### Chips
494
495
Chip input component for tags and multi-value inputs.
496
497
```javascript { .api }
498
/**
499
* Chips component for tag input
500
* @param el - Chips container element
501
* @param options - Configuration options
502
*/
503
class Chips {
504
constructor(el: Element, options?: ChipsOptions);
505
506
static init(els: Element | NodeList, options?: ChipsOptions): Chips | Chips[];
507
static getInstance(el: Element): Chips;
508
static get defaults(): ChipsOptions;
509
510
/** Get chips data */
511
getData(): ChipData[];
512
513
/** Add chip */
514
addChip(chip: ChipData): void;
515
516
/** Delete chip by index */
517
deleteChip(chipIndex: number): void;
518
519
/** Select chip by index */
520
selectChip(chipIndex: number): void;
521
522
destroy(): void;
523
}
524
525
interface ChipData {
526
tag: string;
527
image?: string;
528
}
529
530
interface ChipsOptions {
531
/** Initial chip data */
532
data?: ChipData[];
533
534
/** Placeholder text */
535
placeholder?: string;
536
537
/** Secondary placeholder text */
538
secondaryPlaceholder?: string;
539
540
/** Autocomplete options */
541
autocompleteOptions?: AutocompleteOptions;
542
543
/** Maximum number of chips */
544
limit?: number; // default: Infinity
545
546
/** Callbacks */
547
onChipAdd?: (element: Element, chip: Element) => void;
548
onChipSelect?: (element: Element, chip: Element) => void;
549
onChipDelete?: (element: Element, chip: Element) => void;
550
}
551
```
552
553
**Usage Examples:**
554
555
```html
556
<!-- Basic chips -->
557
<div class="chips"></div>
558
559
<!-- Chips with initial data -->
560
<div class="chips chips-initial"></div>
561
562
<!-- Chips with placeholder -->
563
<div class="chips chips-placeholder"></div>
564
```
565
566
```javascript
567
// Initialize chips
568
const elems = document.querySelectorAll('.chips');
569
M.Chips.init(elems);
570
571
// Chips with initial data
572
const chipInitial = document.querySelector('.chips-initial');
573
M.Chips.init(chipInitial, {
574
data: [
575
{ tag: 'Apple' },
576
{ tag: 'Microsoft' },
577
{ tag: 'Google' }
578
]
579
});
580
581
// Chips with placeholder and autocomplete
582
const chipPlaceholder = document.querySelector('.chips-placeholder');
583
M.Chips.init(chipPlaceholder, {
584
placeholder: 'Enter a tag',
585
secondaryPlaceholder: '+Tag',
586
autocompleteOptions: {
587
data: {
588
'JavaScript': null,
589
'TypeScript': null,
590
'Python': null,
591
'Java': null
592
}
593
}
594
});
595
596
// Get chip data
597
const instance = M.Chips.getInstance(chipPlaceholder);
598
const chipData = instance.getData();
599
console.log('Current chips:', chipData);
600
```
601
602
### Range Slider
603
604
Enhanced range input with Material Design styling.
605
606
```javascript { .api }
607
/**
608
* Range slider component
609
* @param el - Input range element
610
* @param options - Configuration options (currently empty)
611
*/
612
class Range {
613
constructor(el: Element, options?: {});
614
615
static init(els: Element | NodeList, options?: {}): Range | Range[];
616
static getInstance(el: Element): Range;
617
static get defaults(): {};
618
619
destroy(): void;
620
}
621
```
622
623
**Usage Examples:**
624
625
```html
626
<!-- Basic range slider -->
627
<form action="#">
628
<p class="range-field">
629
<input type="range" id="test5" min="0" max="100" />
630
</p>
631
</form>
632
```
633
634
```javascript
635
// Initialize range sliders
636
const elems = document.querySelectorAll('input[type=range]');
637
M.Range.init(elems);
638
```
639
640
## Form Validation
641
642
### CSS Validation Classes
643
644
```css { .api }
645
/* Validation states */
646
.valid {
647
/* Applied to valid inputs */
648
border-bottom: 1px solid #4CAF50;
649
box-shadow: 0 1px 0 0 #4CAF50;
650
}
651
652
.invalid {
653
/* Applied to invalid inputs */
654
border-bottom: 1px solid #F44336;
655
box-shadow: 0 1px 0 0 #F44336;
656
}
657
658
/* Helper text */
659
.helper-text {
660
/* Helper text styling */
661
font-size: 0.8rem;
662
color: rgba(0,0,0,0.54);
663
}
664
665
.helper-text.invalid {
666
/* Error message styling */
667
color: #F44336;
668
}
669
```
670
671
### JavaScript Validation
672
673
```javascript
674
// Manual field validation
675
const emailField = document.getElementById('email');
676
const isValid = M.validate_field(emailField);
677
678
// Update all text fields (useful after AJAX content)
679
M.updateTextFields();
680
681
// Custom validation function
682
function validateForm(form) {
683
let isValid = true;
684
const inputs = form.querySelectorAll('input[type="text"], input[type="email"]');
685
686
inputs.forEach(input => {
687
if (!M.validate_field(input)) {
688
isValid = false;
689
}
690
});
691
692
return isValid;
693
}
694
```
695
696
## Common Form Patterns
697
698
### Dynamic Form Updates
699
700
```javascript
701
// After adding form elements dynamically
702
M.updateTextFields();
703
M.FormSelect.init(document.querySelectorAll('select'));
704
M.CharacterCounter.init(document.querySelectorAll('input[data-length], textarea[data-length]'));
705
706
// Reinitialize specific components
707
const newDatepickers = document.querySelectorAll('.datepicker:not([data-select-id])');
708
M.Datepicker.init(newDatepickers);
709
```
710
711
### Form Submission Handling
712
713
```javascript
714
document.getElementById('myForm').addEventListener('submit', function(e) {
715
e.preventDefault();
716
717
// Validate all fields
718
const inputs = this.querySelectorAll('input.validate');
719
let allValid = true;
720
721
inputs.forEach(input => {
722
if (!M.validate_field(input)) {
723
allValid = false;
724
}
725
});
726
727
if (allValid) {
728
// Process form submission
729
const formData = new FormData(this);
730
console.log('Form is valid, submitting...', formData);
731
} else {
732
M.toast({html: 'Please fix validation errors'});
733
}
734
});
735
```