HTML 从值中输入集合类型
HTML input set type from value
是否有任何已知的方法通过检查为输入设置的默认值来获取输入标签类型?
即如果输入默认值设置为 true 或 false 输入类型应该是复选框并且对于日期和所有其他类型都相同
这是我要检测字段值的代码(有date props和bool props,但它们都呈现为type text input)
Object.keys(user).map(key => <h4>{key} : <input type="auto-detect" value={user[key]} /></h4>)
这是我从 api
得到的 user
的值
{
id: 18273009, // input type="number"
firstName: "admin", // input type="text"
lastName: "user", // input type="text"
email: "admin@gmail.com", // input type="email"
phone: "9178888888", // input type="tel"
address: "123 abc Dr", // input type="text"
isLogged: "b2e74b00-e1a6-44bd-833f-7dba2e98c10e", // input type="text"
loginTime: "2022-04-12T17:20:00.2176106", // input type="datetime"
dateJoined: "2022-03-30T14:12:13.1303726", // input type="datetime"
isMember: false, // input type="checkbox"
isAdmin: false, // input type="checkbox"
isSysAdmin: false, // input type="checkbox"
planType: "Free", // input type="text"
streamsToday: 194, // input type="number"
listIpAddress: [], // don't render, or render <select>
}
仅根据值来确定类型是不可行的。恰当的例子 - 考虑 firstName
和 email
都具有值 admin
。因此,以下内容就足够了。
function autoDetectInputType ( { key, value } ) {
// a switch statement would be fine as well if you like
if ( key === 'email' ) return 'email';
if ( /(?:time|date)/.test( key ) ) return 'datetime';
if ( 'number' === typeof ( value ) ) return 'number';
// and so on and so forth...
}
创建表单控件
来自对象文字 Key/Value 对
user
对象中的一些值无效(您至少应该为 email
提供一个电子邮件地址,为 phone
提供 10 位数字)。所以我已经更正并修改了 user
对象,以便它可以模拟真实数据。
type="datetime"
已弃用,请使用 type="datetime-local"
图一
下例中有 7 个函数:
Function
Purpose
formatDate(d)
Date values must be formatted before being set to a type="datetime-local"
input.
typeFilter(k, v)
Determines what type of form control should be used according to value.
formControl(k, v, t)
Generates an object for each key/value. Each object has a value of a htmlString of a form control
userData(o)
Processes returns from typeFilter()
and formControl()
and will return a single object that contains all of the htmlSrings.
parseHTML(h, n, p=)
Parses htmlString into real HTML.
makeLabel(n, p=)
Creates a <label>
setID(n, a=)
Set's/changes attributes of an element
详情见下方示例
const user = {
idn: 18273009, // input type="number"
first: 'Zer0', // input type="text"
last: '0ne', // input type="text"
email: 'admin@mail.com', // input type="email"
phone: 14085551234, // input type="tel"
address: '1234 Main St. Springfield, IL 62777', // textarea
logHash: 'b2e74b00-e1a6-44bd-833f-7dba2e98c10e', // textarea
timeLogin: '2022-04-12T17:20:00.2176106', // input type="datetime-local"
dateJoined: '2022-03-30T14:12:13.1303726', // input type="datetime-local"
isMember: true, // input type="checkbox"
isAdmin: false, // input type="checkbox"
isSysAdmin: false, // input type="checkbox"
servicePlan: 'Free', // input type="text"
currentStreams: 194, // input type="number"
listIpAddress: ['127.0.0.1', '155.120.3.2', '213.11.77.6'], // select
};
/**
* timeLogin and dateJoined values must be formatted in
* in order to be assigned as a value of an
* <input type="datetime-local">
* LINE 90
*/
const formatDate = (dateTime) => {
const d = new Date(dateTime);
d.setMinutes(d.getMinutes() - d.getTimezoneOffset());
return d.toISOString().slice(0, 16);
};
/**
* A <key/value> pair is passed and a <key/value/tag> trine is
* returned. It checks values to determine which type of
* form control is needed. A string selector of a tagName or
* a string value of the type property is the <tag> of a
* return.
* Line 122
*/
const typeFilter = (key, val) => {
return Array.isArray(val)
? [key, val, 'select']
: Number.isInteger(val) &&
val.toString().length === 11 &&
val.toString().charAt(0) === '1'
? [key, val, 'tel']
: val.valueOf() === true || val.valueOf() === false
? [key, val, 'checkbox']
: val.toString().includes('@')
? [key, val, 'email']
: Number.isInteger(val)
? [key, val, 'number']
: Date.parse(val)
? [key, val, 'datetime-local']
: typeof val === 'string' && val.length >= 20
? [key, val, 'textarea']
: [key, val, 'text'];
};
/**
* Passes <key/value/tag> trine and returns an array of objects
* -- each object contains a key and htmlString value. Each
* htmlString represents a form control:
* ex. <input name="<key>" type="<tag>" value="<value>">
* LINE 134
*/
const formControl = (key, value, tag) => {
const html = {};
let htmlStr;
switch (tag) {
case 'select':
htmlStr = `<select name="${key}"><option selected>Select an option</option>`;
value.forEach((val) => (htmlStr += `<option>${val}</option>`));
html[key] = htmlStr + `</select>`;
break;
case 'tel':
let val = '' + value;
let hyphenated = val
.split('')
.map((n, i) =>
i === 1
? '-' + n
: i === 4
? '-' + n
: i === 7
? '-' + n
: n
)
.join('');
htmlStr = `<input name='${key}' type='tel' value='${hyphenated}'>`;
html[key] = htmlStr;
break;
case 'checkbox':
let end = value === true ? ` checked>` : ` >`;
htmlStr = `<input name='${key}' type='checkbox'` + end;
html[key] = htmlStr;
break;
case 'email':
htmlStr = `<input name='${key}' type='email' value='${value}'>`;
html[key] = htmlStr;
break;
case 'number':
htmlStr = `<input name='${key}' type='number' value='${value}'>`;
html[key] = htmlStr;
break;
case 'datetime-local':
let dateTime = formatDate(value);
htmlStr = `<input name='${key}' type='datetime-local' value='${dateTime}'>`;
html[key] = htmlStr;
break;
case 'textarea':
htmlStr = `<textarea name='${key}' rows='3' cols='20'>${value}</textarea>`;
html[key] = htmlStr;
break;
case 'text':
htmlStr = `<input name='${key}' type='text' value='${value}'>`;
html[key] = htmlStr;
break;
default:
break;
}
return html;
};
/**
* This is the main function that passes in the <user> object
* and returns a modified <user> object -- the values are
* htmlStrings of form controls with the original values embeded
* in the htmlStrings.
*/
const userData = (obj) => {
let types = [];
/*
- Converts <user> object into an array of pairs.
- On each iteration, the return of typeFilter() is stored in
an array <types>.
*/
for (const [key, value] of Object.entries(obj)) {
types.push(typeFilter(key, value));
}
// console.log(types);
/*
- The <types> array is ran through with .map() and .reduce()
- .map() will run formControl() on each trine of <types> --
the return value is an object with one key and one value.
- .reduce() will merge all of those objects into a single
object
- The single object is then returned.
*/
let html = types
.map(([key, value, tag]) => formControl(key, value, tag))
.reduce((obj, htm) => Object.assign(obj, htm), {});
// console.log(html);
return html;
};
/**
* Just a wrapper for .insertAdjacentHTML().
* @param position is optional, if not defined it defaults to
* 'beforeend'.
*/
const parseHTML = (htmlString, node, position = 'beforeend') => {
node.insertAdjacentHTML(position, htmlString);
};
/**
* Creates a <label> with a formatted name of the element it is
* paired with.
*/
const makeLabel = (node, position = 'beforebegin') => {
let text = node.name.replace(/.*?([A-Z][a-z]*)/g, ' ');
parseHTML(`<br><label>${text}: </label>`, node, position);
if (node.matches('textarea')) {
const vert = node.previousElementSibling;
vert.style.verticalAlign = 'top';
}
};
/**
* Sets any given attribute's value of a given element. Note, the
* second @param is an array that should have the attribute's
* name and value. If not defined, then it will pass a default:
* ex. ['id', 'IDN18273009x1650056016986']
* The value is "IDN"(user.idn)"x"(UNIX timestamp)
*/
const setID = (node, attr = ['id', 'IDN' + user.idn +'x'+ Date.now()]) =>
node.setAttribute(attr[0], attr[1]);
const html = userData(user);
parseHTML(`<form></form>`, document.body);
const F = document.forms[0];
const FC = F.elements;
parseHTML(html.listIpAddress, F);
parseHTML(html.isMember, F);
parseHTML(html.isSysAdmin, F);
parseHTML(html.address, F);
parseHTML(html.phone, F);
const ipList = FC.listIpAddress;
const member = FC.isMember;
const sysAdmin = FC.isSysAdmin;
const address = FC.address;
const phone = FC.phone;
makeLabel(ipList);
makeLabel(member);
makeLabel(sysAdmin);
makeLabel(address);
makeLabel(phone);
parseHTML(html.dateJoined, ipList, 'afterend');
const joined = FC.dateJoined;
makeLabel(joined);
setID(F);
html {
font: 2ch/1 'Segoe UI';
}
input,
textarea,
select,
label {
display: inline-block;
margin: 0 0.5rem 0.5rem 0;
font: inherit;
}
label {
vertical-align: baseline;
min-width: 9ch;
text-align: right;
text-transform: capitalize;
}
[type='checkbox'] {
margin: 0;
vertical-align: middle;
}
[type='tel'] {
width: 13.5ch;
}
是否有任何已知的方法通过检查为输入设置的默认值来获取输入标签类型?
即如果输入默认值设置为 true 或 false 输入类型应该是复选框并且对于日期和所有其他类型都相同
这是我要检测字段值的代码(有date props和bool props,但它们都呈现为type text input)
Object.keys(user).map(key => <h4>{key} : <input type="auto-detect" value={user[key]} /></h4>)
这是我从 api
得到的user
的值
{
id: 18273009, // input type="number"
firstName: "admin", // input type="text"
lastName: "user", // input type="text"
email: "admin@gmail.com", // input type="email"
phone: "9178888888", // input type="tel"
address: "123 abc Dr", // input type="text"
isLogged: "b2e74b00-e1a6-44bd-833f-7dba2e98c10e", // input type="text"
loginTime: "2022-04-12T17:20:00.2176106", // input type="datetime"
dateJoined: "2022-03-30T14:12:13.1303726", // input type="datetime"
isMember: false, // input type="checkbox"
isAdmin: false, // input type="checkbox"
isSysAdmin: false, // input type="checkbox"
planType: "Free", // input type="text"
streamsToday: 194, // input type="number"
listIpAddress: [], // don't render, or render <select>
}
仅根据值来确定类型是不可行的。恰当的例子 - 考虑 firstName
和 email
都具有值 admin
。因此,以下内容就足够了。
function autoDetectInputType ( { key, value } ) {
// a switch statement would be fine as well if you like
if ( key === 'email' ) return 'email';
if ( /(?:time|date)/.test( key ) ) return 'datetime';
if ( 'number' === typeof ( value ) ) return 'number';
// and so on and so forth...
}
创建表单控件
来自对象文字 Key/Value 对
user
对象中的一些值无效(您至少应该为 email
提供一个电子邮件地址,为 phone
提供 10 位数字)。所以我已经更正并修改了 user
对象,以便它可以模拟真实数据。
type="datetime"
已弃用,请使用 type="datetime-local"
图一
下例中有 7 个函数:
Function | Purpose |
---|---|
formatDate(d) |
Date values must be formatted before being set to a type="datetime-local" input. |
typeFilter(k, v) |
Determines what type of form control should be used according to value. |
formControl(k, v, t) |
Generates an object for each key/value. Each object has a value of a htmlString of a form control |
userData(o) |
Processes returns from typeFilter() and formControl() and will return a single object that contains all of the htmlSrings. |
parseHTML(h, n, p=) |
Parses htmlString into real HTML. |
makeLabel(n, p=) |
Creates a <label> |
setID(n, a=) |
Set's/changes attributes of an element |
详情见下方示例
const user = {
idn: 18273009, // input type="number"
first: 'Zer0', // input type="text"
last: '0ne', // input type="text"
email: 'admin@mail.com', // input type="email"
phone: 14085551234, // input type="tel"
address: '1234 Main St. Springfield, IL 62777', // textarea
logHash: 'b2e74b00-e1a6-44bd-833f-7dba2e98c10e', // textarea
timeLogin: '2022-04-12T17:20:00.2176106', // input type="datetime-local"
dateJoined: '2022-03-30T14:12:13.1303726', // input type="datetime-local"
isMember: true, // input type="checkbox"
isAdmin: false, // input type="checkbox"
isSysAdmin: false, // input type="checkbox"
servicePlan: 'Free', // input type="text"
currentStreams: 194, // input type="number"
listIpAddress: ['127.0.0.1', '155.120.3.2', '213.11.77.6'], // select
};
/**
* timeLogin and dateJoined values must be formatted in
* in order to be assigned as a value of an
* <input type="datetime-local">
* LINE 90
*/
const formatDate = (dateTime) => {
const d = new Date(dateTime);
d.setMinutes(d.getMinutes() - d.getTimezoneOffset());
return d.toISOString().slice(0, 16);
};
/**
* A <key/value> pair is passed and a <key/value/tag> trine is
* returned. It checks values to determine which type of
* form control is needed. A string selector of a tagName or
* a string value of the type property is the <tag> of a
* return.
* Line 122
*/
const typeFilter = (key, val) => {
return Array.isArray(val)
? [key, val, 'select']
: Number.isInteger(val) &&
val.toString().length === 11 &&
val.toString().charAt(0) === '1'
? [key, val, 'tel']
: val.valueOf() === true || val.valueOf() === false
? [key, val, 'checkbox']
: val.toString().includes('@')
? [key, val, 'email']
: Number.isInteger(val)
? [key, val, 'number']
: Date.parse(val)
? [key, val, 'datetime-local']
: typeof val === 'string' && val.length >= 20
? [key, val, 'textarea']
: [key, val, 'text'];
};
/**
* Passes <key/value/tag> trine and returns an array of objects
* -- each object contains a key and htmlString value. Each
* htmlString represents a form control:
* ex. <input name="<key>" type="<tag>" value="<value>">
* LINE 134
*/
const formControl = (key, value, tag) => {
const html = {};
let htmlStr;
switch (tag) {
case 'select':
htmlStr = `<select name="${key}"><option selected>Select an option</option>`;
value.forEach((val) => (htmlStr += `<option>${val}</option>`));
html[key] = htmlStr + `</select>`;
break;
case 'tel':
let val = '' + value;
let hyphenated = val
.split('')
.map((n, i) =>
i === 1
? '-' + n
: i === 4
? '-' + n
: i === 7
? '-' + n
: n
)
.join('');
htmlStr = `<input name='${key}' type='tel' value='${hyphenated}'>`;
html[key] = htmlStr;
break;
case 'checkbox':
let end = value === true ? ` checked>` : ` >`;
htmlStr = `<input name='${key}' type='checkbox'` + end;
html[key] = htmlStr;
break;
case 'email':
htmlStr = `<input name='${key}' type='email' value='${value}'>`;
html[key] = htmlStr;
break;
case 'number':
htmlStr = `<input name='${key}' type='number' value='${value}'>`;
html[key] = htmlStr;
break;
case 'datetime-local':
let dateTime = formatDate(value);
htmlStr = `<input name='${key}' type='datetime-local' value='${dateTime}'>`;
html[key] = htmlStr;
break;
case 'textarea':
htmlStr = `<textarea name='${key}' rows='3' cols='20'>${value}</textarea>`;
html[key] = htmlStr;
break;
case 'text':
htmlStr = `<input name='${key}' type='text' value='${value}'>`;
html[key] = htmlStr;
break;
default:
break;
}
return html;
};
/**
* This is the main function that passes in the <user> object
* and returns a modified <user> object -- the values are
* htmlStrings of form controls with the original values embeded
* in the htmlStrings.
*/
const userData = (obj) => {
let types = [];
/*
- Converts <user> object into an array of pairs.
- On each iteration, the return of typeFilter() is stored in
an array <types>.
*/
for (const [key, value] of Object.entries(obj)) {
types.push(typeFilter(key, value));
}
// console.log(types);
/*
- The <types> array is ran through with .map() and .reduce()
- .map() will run formControl() on each trine of <types> --
the return value is an object with one key and one value.
- .reduce() will merge all of those objects into a single
object
- The single object is then returned.
*/
let html = types
.map(([key, value, tag]) => formControl(key, value, tag))
.reduce((obj, htm) => Object.assign(obj, htm), {});
// console.log(html);
return html;
};
/**
* Just a wrapper for .insertAdjacentHTML().
* @param position is optional, if not defined it defaults to
* 'beforeend'.
*/
const parseHTML = (htmlString, node, position = 'beforeend') => {
node.insertAdjacentHTML(position, htmlString);
};
/**
* Creates a <label> with a formatted name of the element it is
* paired with.
*/
const makeLabel = (node, position = 'beforebegin') => {
let text = node.name.replace(/.*?([A-Z][a-z]*)/g, ' ');
parseHTML(`<br><label>${text}: </label>`, node, position);
if (node.matches('textarea')) {
const vert = node.previousElementSibling;
vert.style.verticalAlign = 'top';
}
};
/**
* Sets any given attribute's value of a given element. Note, the
* second @param is an array that should have the attribute's
* name and value. If not defined, then it will pass a default:
* ex. ['id', 'IDN18273009x1650056016986']
* The value is "IDN"(user.idn)"x"(UNIX timestamp)
*/
const setID = (node, attr = ['id', 'IDN' + user.idn +'x'+ Date.now()]) =>
node.setAttribute(attr[0], attr[1]);
const html = userData(user);
parseHTML(`<form></form>`, document.body);
const F = document.forms[0];
const FC = F.elements;
parseHTML(html.listIpAddress, F);
parseHTML(html.isMember, F);
parseHTML(html.isSysAdmin, F);
parseHTML(html.address, F);
parseHTML(html.phone, F);
const ipList = FC.listIpAddress;
const member = FC.isMember;
const sysAdmin = FC.isSysAdmin;
const address = FC.address;
const phone = FC.phone;
makeLabel(ipList);
makeLabel(member);
makeLabel(sysAdmin);
makeLabel(address);
makeLabel(phone);
parseHTML(html.dateJoined, ipList, 'afterend');
const joined = FC.dateJoined;
makeLabel(joined);
setID(F);
html {
font: 2ch/1 'Segoe UI';
}
input,
textarea,
select,
label {
display: inline-block;
margin: 0 0.5rem 0.5rem 0;
font: inherit;
}
label {
vertical-align: baseline;
min-width: 9ch;
text-align: right;
text-transform: capitalize;
}
[type='checkbox'] {
margin: 0;
vertical-align: middle;
}
[type='tel'] {
width: 13.5ch;
}