对于前端知识,需要进一步巩固和加强,进入企业之后,要具备一定的接口调试,参数接收的能力,以及单体页面的开发,这里我学习一下前端知识巩固一下自身的技术栈和水平。本次笔记是跟学黑马的同名课程,巩固一下自己的前端知识水平,希望入职能用到。
Java 程序员一提起前端知识,心情那是五味杂陈,百感交集。
这门课为什么不由前端老师来讲?
课程安排
HTML 是什么:即 HyperText Markup language 超文本标记语言,咱们熟知的网页就是用它编写的,HTML 的作用是定义网页的内容和结构。
什么是HyperText呢,我们看下资料提供的课件。
详见 代码\第1章\example1-什么是html和css 1.html
点击网页2
那什么是Markup language呢?
我们右键,以VS Code打开。
这些以尖括号括起来的部分,我们都称之为Markup。在前端中,我们称之为标签。
<标签>
的方式赋予内容不同的功能和含义CSS 是什么:即 Cascading Style Sheets 级联(层叠)样式表,它描述了网页的表现与展示效果
我们引入style.css样式。
可以看到超链接的字体和背景颜色发生了变化。
HTML 由一系列元素 elements
组成,例如
<p>Hello, world!p>
整体称之为元素
和
标签包围起来的 Hello, world 称之为内容
p 是预先定义好的 html 标签,作用是将内容作为一个单独的段落
在 代码\第1章\example2-html元素下新建文件3.html我们自己写一个元素
代码如下:
<p>Hello, world!p>
用浏览器打开
效果如下:
如果我们想增加标签数量,通过快捷键复制
ALT + SHIFT + ↓
展示结果如下:
这里小提示:如果我们熟悉IDEA的快捷键,我们可以通过下载插件,改变快捷键习惯为IDEA的
元素还可以有属性,如
<p id="p1">Hello, world! 1p>
<p id="p2">Hello, world! 2p>
<p id="p3">Hello, world! 3p>
<p id="p4">Hello, world! 4p>
<p id="p1" title="标题1">Hello, world! 1p>
<p id="p2">Hello, world! 2p>
<p id="p3">Hello, world! 3p>
<p id="p4">Hello, world! 4p>
元素之间可以嵌套,如
<p id="p1" title="标题1">Hello, world! 1 I am <b>Conyb>p>
<p id="p2">Hello, world! 2p>
<p id="p3">Hello, world! 3p>
<p id="p4">Hello, world! 4p>
错误嵌套写法:
标签不能交叉
<p>HTML 是一门非常<b>强大的语言p>b>
不包含内容的元素称之为空元素,如
<img src="1.png">
<img src="1.png"/>
前面介绍的只是单独的 HTML 元素,它们可以充当一份完整的 HTML 页面的组成部分
DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>测试页面title>
head>
<body>
<p id="p1">Hello, world!p>
<img src="1.png">
body>
html>
html
元素囊括了页面中所有其它元素,整个页面只需一个,称为根元素head
元素包含的是那些不用于展现内容的元素,如 title
,link
,meta(指定编码)
等body
元素包含了对用户展现内容的元素,例如后面会学到的用于展示文本、图片、视频、音频的各种元素在 代码\第1章\example3-html页面 目录下新建网页文件2.html
快捷键!,可以直接生成html的模版
这里我们修改代码为:
DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>这是一个完整页面title>
head>
<body>
<p>Hello Worldp>
<img src="1.png">
body>
html>
图片宽度比较大,我们重新设置一下宽度的像素
<img src="1.png" width="300">
<h1>1号标题h1>
<h2>2号标题h2>
<h3>3号标题h3>
<h4>4号标题h4>
<h5>5号标题h5>
<h6>6号标题h6>
<p>段落p>
代码如下
<p id="p1" title="悬浮1">段落1p>
<p id="p2" title="悬浮2">段落2p>
<p id="p3" title="悬浮3">段落3p>
无序列表 unordered list
<ul>
<li>列表项1li>
<li>列表项2li>
<li>列表项3li>
ul>
有序列表
ordered list
<ol>
<li>列表项1li>
<li>列表项2li>
<li>列表项3li>
ol>
多级列表
<ul>
<li>
北京市
<ul>
<li>海淀区li>
<li>朝阳区li>
<li>昌平区li>
ul>
li>
<li>
河北省
<ul>
<li>石家庄li>
<li>保定li>
ul>
li>
ul>
锚,超链接
<a href="网页地址">超链接文本a>
本地网页(同目录)
<a href="2.html">本地网页a>
<a href="http://www.baidu.com">互联网网页a>
<a href="#p1">页面内锚点a>
<hr>
<br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br>
<p id="p1">很下面的内容 <a href="#">回到顶部a>p>
<img src="文件路径">
src 格式有 3 种
比如
<img src="heima.png">
data:媒体类型;base64,数据
这里要把图片转换为Base64的二进制数据,具体方法如下
@Test
public void test(){
String path = "E:\\黑马\\前端\\代码\\代码\\第1章\\example4-常见元素\\heima.png";
byte[] bytes = null;
try {
Path path2 = Paths.get(path);
bytes = Files.readAllBytes(path2);
// 转换成BASE64Encoder
BASE64Encoder encoder = new BASE64Encoder();
String content = encoder.encode(bytes);
System.out.println(content);
} catch (IOException e) {
e.printStackTrace();
}
}
<video src="文件路径">video>
代码如下:
<video src="test.mp4" width="300" controls autoplay>video>
<audio src="文件路径">audio>
代码如下:
<audio src="bgm.mp3" controls>audio>
页面如下:
表单的作用:收集用户填入的数据,并将这些数据提交给服务器
表单的语法
<form action="服务器地址" method="请求方式" enctype="数据格式">
<input type="submit" value="提交按钮">
form>
<input type="text" name="uesrname">
这里我们自己写一个表单
<form action="https://www.baidu.com/s">
<input type="text" name="wd">
<input type="submit" value="搜索">
form>
<hr>
加深难度,结合springboot
前端:
<form action="http://localhost:8077/test">
<input type="text" name="username">
<input type="submit" name="提交">
form>
后端代码:
@Controller
public class MyController {
@RequestMapping("/test")
@ResponseBody
public String test(String username) {
System.out.println("username:" + username);
return "收到数据";
}
}
这里 @Controller + @ResponseBody 可以用@RestController替代
前端页面:
<input type="password" name="password">
增加密码框
<form action="http://localhost:8077/test">
账号:<input type="text" name="username">
<hr>
密码:<input type="password" name="password">
<input type="submit" name="提交">
form>
<input type="hidden" name="id">
页面中没有显示
那如果我们想通过隐藏框传递值呢,这里我们可以给隐藏框赋值。
<input type="hidden" name="id" value="1">
前台代码如下:
<form action="http://localhost:8077/test">
账号:<input type="text" name="username">
<hr>
密码:<input type="password" name="password">
<br>
<input type="submit" name="提交">
<hr>
<input type="hidden" name="id" value="1">
form>
后台代码:
@RequestMapping("/test")
public String test(User user) {
System.out.println("username:" + user.getUsername());
System.out.println("id:" + user.getId());
return "收到数据";
}
新建一个实体类
/**
* @ClassName: User
* @Description:
* @Author: wty
* @Date: 2023/7/13
*/
public class User {
private String username;
private String password;
private Integer id;
private LocalDate birthday;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public LocalDate getBirthday() {
return birthday;
}
public void setBirthday(LocalDate birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", id=" + id +
", birthday=" + birthday +
'}';
}
}
<input type="date" name="birthday">
后台增加属性
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate birthday;
public LocalDate getBirthday() {
return birthday;
}
public void setBirthday(LocalDate birthday) {
this.birthday = birthday;
}
<input type="radio" name="sex" value="男" checked>
<input type="radio" name="sex" value="女">
页面如下:
男<input type="radio" name="sex" value="男" checked>
女<input type="radio" name="sex" value="女">
checked属性表示,界面加载过程中默认选中一个。
界面如下:
后台输出如下:
<input type="checkbox" name="fav" value="唱歌">
<input type="checkbox" name="fav" value="逛街">
<input type="checkbox" name="fav" value="游戏">
前台如下:
唱歌<input type="checkbox" name="hobby" value="唱歌">
逛街<input type="checkbox" name="hobby" value="逛街">
游戏<input type="checkbox" name="hobby" value="游戏">
<hr>
后端如下:
private List<String> hobby;
public List<String> getHobby() {
return hobby;
}
public void setHobby(List<String> hobby) {
this.hobby = hobby;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", id=" + id +
", sex='" + sex + '\'' +
", birthday=" + birthday +
", hobby=" + hobby +
'}';
}
<input type="file" name="avatar">
注意:上传文件必须加如下属性
<form action="http://localhost:8077/test" method="post" enctype="multipart/form-data">
<input type="file" name="avatar">
form>
后台如下:
private MultipartFile avatar;
@Override
public MultipartFile getAvatar() {
return avatar;
}
public void setAvatar(MultipartFile avatar) {
this.avatar = avatar;
}
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", id=" + id +
", sex='" + sex + '\'' +
", birthday=" + birthday +
", hobby=" + hobby +
", avatar=" + avatar.getSize() +
'}';
}
请求由三部分组成
红色标注
这里可以看到 GET /test2?name=tony HTTP/1.1 叫请求行
POST是请求方式
/test2?name=tony是URI
HTTP/1.1是协议版本
绿色标注
请求头:
格式:头名:头值
localhost:虚拟主机
蓝色标注的,并不是请求都会携带
可以用 telnet 程序测试
GET /test2?name=%E5%BC%A0&age=20 HTTP/1.1
Host: localhost
java后台这么写
@RequestMapping("/test2")
@ResponseBody
public String test2(String name) {
System.out.println("name:" + name);
return "收到:" + name;
}
这里我们这么发送
GET /test2?name=tony HTTP/1.1
Host: localhost
这里要用虚拟机安装telnet,这里我的是CentOS,指令如下:
yum install telnet -y
安装好后,用虚拟机连接windows本机
telnet ip 端口号
回车,连接上后,输入
GET /test2?name=tony HTTP/1.1
Host: localhost
POST /test2 HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 21
name=%E5%BC%A0&age=18
application/x-www-form-urlencoed 格式细节:
这里我们测试一下,请求如下:
POST /test2 HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 10
name=zhang
这里Content-Length = 10不是随便定义的,根据name=zhang一个英文字符占一个长度,共10个字节。
如果参数是多个呢,这里我们修改一下Java后端的代码
@RequestMapping("/test22")
@ResponseBody
public String test22(String name, Integer age) {
System.out.println("name:" + name + ",age:" + age);
return "收到name:" + name + ",age:" + age;
}
telnet测试如下:
POST /test22 HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Content-Length: 16
name=Lucy&age=18
输出如下:
这里Content-Length是多少,我们可以简单学习javascript
打开浏览器输入F12在控制台中输入:
"name=Lucy&age=18".length
这里我们再尝试一下get请求多参数的情况
GET /test22?name=tony&age=20 HTTP/1.1
Host: localhost
在浏览器按F12,打开控制台
encodeURIComponent("张")
回车后,就出来了汉字的编码
'%E5%BC%A0'
我们再发送一下GET请求
GET /test22?name=%E5%BC%A0&age=20 HTTP/1.1
Host: localhost
收到结果:
那么“张”这个汉字,是怎么变成URL Code的呢?我们研究一下。
也就是说%E5%BC%A0是怎么来的。
我们不妨先用Java代码,把“张”转换成对应的UTF-8格式
@Test
public void test() throws Exception {
// utf-8
byte[] bytes = "张".getBytes(StandardCharsets.UTF_8);
System.out.println(bytes);
for (byte b : bytes) {
System.out.println(Integer.toHexString(Byte.toUnsignedInt(b)));
}
}
输出结果:
[B@50de0926
e5
bc
a0
POST /test3 HTTP/1.1
Host: localhost
Content-Type: application/json
Content-Length: 25
{"name":"zhang","age":18}
json 对象格式
{"属性名":属性值}
其中属性值可以是
json 数组格式
[元素1, 元素2, ...]
我们发送如下:
POST /test3 HTTP/1.1
Host: localhost
Content-Type: application/json
Content-Length: 25
{"name":"zhang","age":18}
Java后台新建实体类
package com.itheima.pojo;
/**
* @ClassName: Req
* @Description:
* @Author: wty
* @Date: 2023/7/13
*/
public class Req {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Req{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Controller如下:
这里注意,接收json格式的数据,必须加上@RequestBody注解。
@RequestMapping("/test3")
@ResponseBody
public Req test3(@RequestBody Req req) {
System.out.println(req);
return req;
}
返回如下:
这里的19和0是分块发送的,最终字符数量是0,就停止发送。
这里发送的时候Content-Length: 25 明明是25
接收的时候怎么变成19
Java后台输出如下:
如果是JDK16,还可以使用Req,替代实体类,但是Req相当于get,初次赋值后不会更改,而没有set方法,是不可变的对象。
@RequestMapping("/test3")
public Req test3(@RequestBody Req req) {
System.out.println(req);
return req;
}
record Req(String name,Integer age){}
注意:json汉字可以照常发送
打开浏览器输入F12,之后输入
'{"name":"张","age":18}'.length
返回:21(字符)
因为传递是按照字节的,UTF-8中,一个汉字是三个字节,所以
21 -1 + 3 = 23
所以Content-Length是23
POST /test3 HTTP/1.1
Host: localhost
Content-Type: application/json
Content-Length: 23
{"name":"张","age":18}
POST /test22 HTTP/1.1
Host: localhost
Content-Type: multipart/form-data; boundary=123
Content-Length: 125
--123
Content-Disposition: form-data; name="name"
lisi
--123
Content-Disposition: form-data; name="age"
30
--123--
--分隔符
--分隔符--
Java后端如下:
@RequestMapping("/test22")
@ResponseBody
public String test22(String name, Integer age) {
System.out.println("name:" + name + ",age:" + age);
return "收到name:" + name + ",age:" + age;
}
telnet结果如下:
这里有个疑问
Content-Length: 125是如何计算的
·--123
Content-Disposition: form-data; name="name"
lisi
--123
Content-Disposition: form-data; name="age"
30
--123--·.length
这里要引入反引号,最后答案是116
116相比于125少了9个字节,那这9个字节差在哪儿呢。就差在换行符上
在换行中其实有两个符号\r和\n,而JavaScript中只计算了一个,而我们的多行数据,刚好是9行,所以要加9
客户端发送
服务端接收
Http 无状态,有会话
服务端使用了 session 技术来暂存数据
GET /s1?name=zhang HTTP/1.1
Host: localhost
Java后端如下:
@RequestMapping("/s1")
@ResponseBody
public String s1(HttpSession session, String name) {
session.setAttribute("name", name);
return "数据已存储";
}
返回数据如下:
jsessionid = 5EC247CB0B301ACBFE90157D6F723CC5
GET /s2 HTTP/1.1
Host: localhost
Cookie: JSESSIONID=5EC247CB0B301ACBFE90157D6F723CC5
Java后端代码如下:
@RequestMapping("/s2")
@ResponseBody
public String s2(HttpSession session) {
return "取出数据" + session.getAttribute("name");
}
session 技术实现身份验证
缺点:不适用于分布式项目,因为分布式项目有多台服务器,如果每一台都存储一份完整session,显然不太合理,这里引出了jwt技术。
jwt 技术实现身份验证
GET /j1?name=zhang&pass=123 HTTP/1.1
Host: localhost
token为
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ6aGFuZyJ9.qmta3qjU3XKd8di9n6h9DhbTznJYb75v0CESA7ZLx0E
Java后端代码
@RequestMapping("/j1")
@ResponseBody
public String j1(String name, String pass) {
if (!StringUtils.isEmpty(name) && !StringUtils.isEmpty(pass)) {
if ("zhang".equals(name) && "123".equals(pass)) {
String token = Jwts.builder().setSubject(name).signWith(key).compact();
return "验证身份通过:" + token;
} else {
return "验证身份失败";
}
}
return "用户名或者密码为空,验证身份失败";
}
GET /j2 HTTP/1.1
Host: localhost
Authorization: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ6aGFuZyJ9.qmta3qjU3XKd8di9n6h9DhbTznJYb75v0CESA7ZLx0E
Java后端代码
@RequestMapping("/j2")
@ResponseBody
public String j2(@RequestHeader String authorization) {
try {
System.out.println(authorization);
Jws<Claims> jws = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(authorization);
return "校验通过, 你是:" + jws.getBody().getSubject();
} catch (Exception e) {
return "校验失败";
}
}
这里有个问题,就是这个长字符串的token有什么用,这里我们在IDEA中编写一个方法
@Test
public void test() {
// header(签名算法) payload(数据) 签名
// eyJzdWIiOiJhZG1pbiJ9
// token分成了3个部分,每个部分由.进行分割
// 第一个部分 eyJhbGciOiJIUzI1NiJ9 header(签名算法)未加密
// 第二个部分 eyJzdWIiOiJ6aGFuZyJ9 payload(数据)未加密
// 第三个部分 qmta3qjU3XKd8di9n6h9DhbTznJYb75v0CESA7ZLx0E 签名 加密
String token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ6aGFuZyJ9.qmta3qjU3XKd8di9n6h9DhbTznJYb75v0CESA7ZLx0E";
// 1 2 3 ==> 6
// 1 4 3 ==> 8
// 第一个部分
System.out.println(new String(Base64.getDecoder().decode("eyJhbGciOiJIUzI1NiJ9")));
// 第二个部分
System.out.println(new String(Base64.getDecoder().decode("eyJzdWIiOiJ6aGFuZyJ9")));
// 第三个部分
// admin 权限大的用户 可以做所有的操作
//String str = "{\"sub\":\"zhang\"}";
// eyJzdWIiOiJ6aGFuZyJ9
String str = "{\"sub\":\"admin\"}";
// eyJzdWIiOiJhZG1pbiJ9
System.out.println(Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8)));
}
我们把中间的权限名称替换一下
替换成admin用户的
GET /j2 HTTP/1.1
Host: localhost
Authorization: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiJ9.qmta3qjU3XKd8di9n6h9DhbTznJYb75v0CESA7ZLx0E
我们访问试了下,发现没有骗过服务器
那为什么服务器能检验出问题呢,主要在第三部分签名上,签名是根据前两个部分生成了秘钥。
//
// 1 2 3 ==> 6
// 1 4 3 ==> 8
即 Cascading Style Sheets,它描述了网页的表现与展示效果
新建一个文件style-exercise.css
p {
background-color: blueviolet;
}
html加上如下代码
<link rel="stylesheet" href="style-exercise.css">
效果如下:段落标签的背景颜色变为了紫色
修改html中需要修改的部分标签
<body>
<p id="p1">1111111111111p>
<p id="p2" class="c2">2222222222222p>
<p id="p3" class="c3">3333333333333p>
body>
修改style-exercise.css
/* class选择器 */
.c2 {
background-color: brown;
}
.c3 {
background-color: chartreuse;
}
修改html文件
<body>
<p id="p1">1111111111111p>
<p id="p2">2222222222222p>
<p id="p3">3333333333333p>
body>
修改style-exercise.css
/* id选择器 */
#p1 {
background-color: cornflowerblue;
}
效果如下:
这里我们不禁要问,如果多个选择器都匹配到了同一个元素,那么优先级是怎么样的呢?
css代码如下:
/* 元素选择器-type */
p {
background-color: blueviolet;
}
/* class选择器 */
.c2 {
background-color: brown;
}
/* id选择器 */
#p2 {
background-color: cornflowerblue;
}
我们都修改段落2的样式
最后效果如下:
结论:
id选择器 > class选择器 > 选择选择器
详见
CSS样式查询
这里简要讲解一下display属性
代码如下:
#p2 {
background-color: cornflowerblue;
display: none;
}
/* id选择器 */
#p2 {
background-color: cornflowerblue;
display: block;
}
与布局相关的 html 元素
资料提供的界面如下:
DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>布局title>
<style>
html,body {
margin:0;
width: 100%;
height: 100%;
text-align: center;
font-size: 30px;
font-weight: bold;
}
div{
box-sizing: border-box;
}
.container {
height: 100%;
position: relative;
}
#header {
background-color:rgb(152, 152, 255);
width: 100%;
height: 80px;
padding-top: 10px;
}
#aside {
background-color:aquamarine;
float: left;
width: 200px;
height: calc(100% - 140px);
padding-top: 10px;
}
#main {
background-color:honeydew;
float: left;
width: calc(100% - 200px);
height: calc(100% - 140px);
padding-top: 10px;
padding-left: 20px;
text-align: left;
}
#footer {
background-color:darksalmon;
height: 60px;
padding-top: 10px;
}
style>
head>
<body>
<div class="container">
<div id="header">#headerdiv>
<div id="aside">#asidediv>
<div id="main">#maindiv>
<div style="clear: both;">div>
<div id="footer">#footerdiv>
div>
body>
html>
效果如图:
这里style可以理解为内部样式,之前我们新建css,通过link引入,这个叫做外部样式。
我们给div id ="main"中增加type类型的文本
<body>
<div class="container">
<div id="header">#headerdiv>
<div id="aside">#asidediv>
<div id="main">
#main
<form action="">
<input type="text" value="文本">
form>
div>
<div style="clear: both;">div>
<div id="footer">#footerdiv>
div>
body>
先看下资料的源码
DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>模板title>
<style>
html,
body {
margin: 0;
width: 100%;
height: 100%;
}
.btn {
padding: 10px;
}
.out {
width: 100%;
height: 100%;
box-sizing: border-box;
background-color:darkgrey;
}
.in {
width: 200px;
box-sizing: border-box;
height: 200px;
border: solid 2px black;
padding: 10px;
background-color: antiquewhite;
margin: 10px;
float: left;
}
style>
head>
<body>
<div class="out">
<div class="btn">
<input type="button" value="根据模板创建" id="add">
div>
div>
<template id="t">
<div class="in">
<form action="">
<p><label>姓名label> <input type="text">p>
<p><label>年龄label> <input type="text">p>
<p><input type="submit" value="添加">p>
form>
div>
template>
<script>
document.getElementById("add").onclick = () => {
let t = document.getElementById("t");
let inputs = t.content.querySelectorAll("input");
inputs[0].value = randomGenerator("abcdefghijklmnopqrstuvwxyz", 5);
inputs[1].value = randomGenerator("1234567890", 2);
const c = document.importNode(t.content, true);
document.querySelector(".out").appendChild(c);
}
function randomGenerator(str, n) {
const result = [];
for (let i = 0; i < n; i++) {
result.push(str.charAt(Math.floor(Math.random() * str.length)))
}
return result.join("");
}
script>
body>
html>