1、前端小练习(一)-------- 实现网页端的文件资源管理器
JS实现资源管理器功能介绍:
内容目录:
- 效果图
- 界面介绍
- 实现原理
- 总结
网页效果图
界面介绍
整个页面比较简单,主要有两个部分组成,
分别是:左边显示文件夹层级目录、右边显示文件内容的显示区域。
实现原理
技术:html+CSS+JS(+JQuery)
组件: 图标样式库 css文件;
解析markdown文件
<link rel="stylesheet" href="../css/all.min.css">
<script src="../js/marked.min.js" type="module"></script>
根据上面的页面介绍我们将页面大致分为两个部分:文件夹层级显示区域、文件内容显示区域
直接上代码:
<body>
<!--显示文件层级目录结构-->
<div id="showFolder">
<!-- 标题栏显示区域 -->
<div id="showTitle">
<i class="fa fa-bars" style="margin-left: 10px;"></i>
<div class="titleText" style="margin-left: 10px;">资源管理器</div>
</div>
<!--文件夹层级显示管理-->
<div class="folderMenu">
<!--打开文件按钮-->
<button id="openFolder">打开文件夹</button>
</div>
</div>
<!--显示文本区域-->
<div id="showTxt">
<div class="showTxt_Title">
<!--扩大显示文件内容的图标-->
<i class="fa fa-expand"></i>
</div>
<!--文件内容主要显示区域-->
<div class="showTxt_Content"></div>
</div>
</body>
因为个人原因给初始页面只是加载两个小按钮,需要点击按钮后进行扩展,下面我直接贴上CSS代码
*{
margin: 0;
padding: 0;
}
body{
position: fixed;
width: 100%;
height: 100%;
background: url("../image/background/personage.jpg")no-repeat;
background-size: cover;
}
/*开始整体框架进行搭建*/
/*首先是左边的资源管理器界面端*/
/*这边进行做一个动画效果*/
#showFolder{
width:50px;
height: 50px;
border-radius: 10px;
background: rgba(0,0,0,.3);
color: white;
user-select: none;
transition: width 1s,height 1s;
overflow: hidden;
}
/*左边上面的标题部分*/
#showFolder #showTitle{
width: 100%;
height:50px;
display: flex;
font-size: 20px;
align-items: center;
overflow: hidden;
}
#showTitle:hover{
cursor: pointer;
}
#showFolder .titleText{
width: calc(100% - 50px);
display: none;
}
/*下面的层级目录的显示修饰部分*/
#showFolder .folderMenu{
margin-top: 10px;
width: 100%;
height: calc(100% - 60px);
transition: height 1s;
text-align: center;
display: none;
overflow-y: visible;
overflow-x:hidden ;
}
/*打开文件按钮的修饰*/
#showFolder .folderMenu Button{
border-radius: 3px;
border-style: none;
text-align: center;
padding: 3px;
width: 98%;
background: #4158D0;
color: white;
}
/* 下面我们需要对文件资源管理器进行修饰管理 */
.folderMenu .folder,.folderMenu .file{
width: 230px;
height: 28px;
display: flex;
align-items: center;
overflow: hidden;
font-size: 14px;
}
.folderMenu .folder:hover,
.folderMenu .file:hover{
cursor: pointer;
color:#FFE4C4;
}
.folderMenu .file .fileName{
text-align: left;
white-space: nowrap;
overflow: hidden;
}
.folderMenu .childen_Area{
margin-left: 20px;
overflow: hidden;
}
/*右边文件内容显示区域*/
#showTxt{
color: white;
font-size: 20px;
position: fixed;
top: 0;
right: 0;
width: 50px;
height: 50px;
border-radius: 20px;
background: rgba(0,0,170,.3);
transition: height 1s,width 1s;
}
/*右边文件内容显示区域中标题栏*/
#showTxt .showTxt_Title{
position: fixed;
top: 0px;
right: 0px;
user-select: none;
width: 50px;
height: 50px;
text-align: center;
line-height: 50px;
}
/*文件内容显示区域中主体部分*/
#showTxt .showTxt_Content{
margin: 60px 10px 5px 30px;
width: calc(100% - 40px);
height: calc(100% - 65px);
overflow-y: auto;
font-family: "华文宋体";
}
JS部分,主要讲一下主要实现思路:
1、首先是用户点击按钮,触发事件。
2、然后JS通过异步调用 window.showDirectoryPicker()函数,获得到选中的文件夹的句柄(handle)
3、创建一个异步函数进行处理这个句柄 (我自己命名)processHandle(handle, Elem),分别有两个参数,分别是需要传递的句柄,需要向某个元素添加内容(也就是需要显示在哪块区域中)。
4、在processHandle(handle, Elem)中我们需要做的事情有下面两个:
~ 通过entires()函数,获取一个异步迭代器
~ 然后就需要遍历这个迭代器(这个时候我们获取的是我们选择的文件夹中子文件以及子文件夹),通过判断迭代器中每个元素中kind属性,进行判断该元素是文件(同时创建文件图标以及显示文件名)还是文件夹(同样的创建下拉图标、文件夹图标以及显示文件夹名字);
5、创建文件图标的函数creatElem_File(root,Elem) :root是当前文件夹句柄,Elem是当前文件夹所对应的document的元素
- 首先创建外面的div进行包裹着整个边框
- 创建 用来显示文件图标
- 创建一个div显示文件名字
- 然后将2和3添加到1中
- 最后将1添加到参数Elem中
- 创建对应的点击事件(点击事件是load线程中,而之前访问夹是异步线程)
6、创建文件夹图标的函数 creatElem_Folder(root,Elem):root是当前文件夹句柄,Elem是当前文件夹所对应的document的元素
- 首先创建外面的div进行包裹着整个边框
- 创建 用来显示下拉图标
- 创建 用来显示文件夹图标
- 创建一个div显示文件名字
- 需要将2和3添加到1中
- 然后将1添加到参数Elem中
- 我们需要再次调用processHandle(handle, Elem)函数,形成递归过程,达到遍历每一个文件夹的目的
- 创建对应的点击事件(点击事件是load线程中,而之前访问夹是异步线程)
下面就是展示JS代码:
window.onload=async () => {
// fa-bar图标的点击事件
$("#showTitle").click(showTitle_Click);
// 打开文件夹的点击事件
$("#openFolder").click(await openFolder_Click);
// 点击expand图标后显示文本区域
$("#showTxt .showTxt_Title .fa").click(expandICON_Click);
}
// 区域动画展示效果函数
function showTitle_Click() {
/* 主要是做一个动画效果 */
const showFolder=$("#showFolder")[0];
const titleText= $(".titleText")[0];
const folderMenu=$("#showFolder .folderMenu")[0];
if(showFolder.clientWidth==50){
showFolder.style.width="200px";
showFolder.style.height="100%";
setTimeout(()=>{titleText.style.display="block";
folderMenu.style.display="block";
folderMenu.style.height="calc(100% - 60px)";
},800);
}else{
// 返回到原来的样式
showFolder.style.width="50px";
showFolder.style.height="50px";
titleText.style.display="none";
folderMenu.style.height="0px";
}
}
/**
* 打开文件夹按钮响应事件
* @returns {Promise<void>}
*/
async function openFolder_Click(){
try{
const handle=await window.showDirectoryPicker();
const folderMenu=$(".folderMenu")[0];
$("#openFolder")[0].style.display="none";
// 然后将handle交给专门的函数进行处理
await processHandle(handle,folderMenu);
}catch (e){
console.log(e)
}
}
/**
* 处理获取本地文件的句柄,运用递归的方式进行遍历旗下所有的子文件以及子目录
* @param handle
* @param Elem
* @returns {Promise<void>}
*/
async function processHandle(handle, Elem) {
const iter = handle.entries();
for await (const item of iter) {
if (item[1].kind == "file") {
creatElem_File(item[1], Elem);
} else {
creatElem_Folder(item[1], Elem);
}
}
return;
}
function creatElem_File(root,Elem) {
// 首先是创建外面包裹的标签
let facting_File=document.createElement("div");
facting_File.className="file";
facting_File.style.marginLeft="10px";
/*
1、文件图标包括:图标+文件名
2、创建每一个文件点击响应事件
*/
// 创建文件夹图标节点
let file_ICON=document.createElement("i");
file_ICON.className="file_ICON fa fa-file";
let file_Name=document.createElement("div");
file_Name.className="fileName";
file_Name.style.marginLeft="5px";
file_Name.prepend(root.name);
facting_File.appendChild(file_ICON);
facting_File.appendChild(file_Name);
Elem.appendChild(facting_File);
// 创建每个文件的对应的点击事件
facting_File.onclick=()=>{
// 从支线线程转到加载线程 此时调用异步线程中对象会包裹成promise对象
const showTxt=$("#showTxt")[0];
const showTxt_Content=$("#showTxt .showTxt_Content")[0]
if(showTxt.clientWidth<=60){
// 首先调用文本区域的显示函数
expandICON_Click();
}
let promise=root.getFile();
// 这边读取时是需要进行分类解析的
/* 首先是创建几个正则表达式,用来检测是否是对应的文件
*/
const reg=/(.csv|.txt)$/m
const reg1=/(.md)$/m;
const reg2=/(.jp[e]g|.png)$/m;
const reader=new FileReader();
promise.then(result=>{
// 然后进入promise对象处理异步线程中进行分门别类进行读取文件
if(reg.test(result.name)){
console.log("读取文件");
reader.readAsText(result,"utf-8");
reader.onload=(evt)=>{
showTxt_Content.innerHTML=evt.target.result;
}
}
if(reg1.test(result.name)){
console.log("读取markdown文件");
reader.readAsText(result,"utf-8");
reader.onload=(evt)=>{
showTxt_Content.innerHTML=marked.parse(evt.target.result);
}
}
if(reg2.test(result.name)){
console.log("读取二进制文件")
reader.readAsDataURL(result);
reader.onload=(evt)=>{
showTxt_Content.innerHTML="";
let image=document.createElement("img");
image.style.width="95%";
image.style.height="80%";
image.style.margin=" 0 auto";
image.src=evt.target.result;
console.log(evt.target.result);
showTxt_Content.appendChild(image);
}
}
});
}
}
function creatElem_Folder(root,Elem){
/*
1、创建文件夹层级包含:箭头、文件夹图标、文件名
2、创建子级隐藏的区域,并且调用process函数进行遍历文件夹层级
*/
// 这是创建外面包裹的边框
let facting_Folder=document.createElement("div");
facting_Folder.className="folder";
// 创建下拉箭头
let xiala=document.createElement("i");
xiala.className="xiala fa fa-circle-chevron-right"
xiala.style.marginLeft="5px";
// 创建文件夹图标
let folder_ICON=document.createElement("i");
folder_ICON.className="folder_ICON fa fa-folder-closed";
folder_ICON.style.marginLeft="5px";
// 创建文件夹名字显示区域
let folder_Name=document.createElement("div");
folder_Name.append(root.name);
folder_Name.style.marginLeft="5px";
facting_Folder.appendChild(xiala);
facting_Folder.appendChild(folder_ICON);
facting_Folder.appendChild(folder_Name);
Elem.appendChild(facting_Folder);
// 然后我们需要创建专门的区域来显示子层级目录
let childen_Area=document.createElement("div");
childen_Area.className="childen_Area";
childen_Area.style.display="none";
Elem.appendChild(childen_Area);
processHandle(root,childen_Area);
// 我们还需要创建对应文件夹打开响应事件
facting_Folder.onclick=()=>{
if(childen_Area.style.display=="none"){
// 首先是将下拉图标的方向进行更换
xiala.className="xiala fa fa-circle-chevron-down";
folder_ICON.className="folder_ICON fa fa-folder-open";
// 然后将这个子层级显示出来
childen_Area.style.display="block";
}else{
xiala.className="xiala fa fa-circle-chevron-right";
folder_ICON.className="folder_ICON fa fa-folder-closed";
// 然后将这个子层级显示出来
childen_Area.style.display="none";
}
}
}
// 显示文本区域动画过度效果
function expandICON_Click() {
const showTxt=$("#showTxt")[0];
if(showTxt.clientWidth<=70){
showTxt.style.width="calc(100% - 200px)";
showTxt.style.height ="100%";
}else{
showTxt.style.width="50px";
showTxt.style.height ="50px";
}
}
总结
主要自己从想出概念,到找到思路然后确定思路(中间花费很多时间),然后到代码落地碰到很多困难。也是幸运地能够实现自己想出来的概念。我自己是从CSDN、博客园以及抖音中无意中看到推荐视频,刚好看到该知识点的视频,解决我自己碰到的问题,幸运儿。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!