disabled属性的作用
我们知道disabled只能作用在表单元素上面,主要作用是使元素将不可变、不能响应事件、不参与约束验证、不能被用户更新、不能聚焦或与表单一同提交,即用户不能与该控件或其后代控件进行交互。
这些元素支持 disabled
属性:<button>
、<fieldset>
、<optgroup>
、<option>
、<select>
、<textarea>
和 <input>
。
如果所支持的元素上存在该属性,将匹配 :disabled
伪类。如果该属性未被包含,则将匹配 :enabled
伪类。如果该元素不支持 disabled 属性,该属性将没有任何作用,包括不会导致被 :disabled
和 :enabled
伪类匹配。
所以如果想要在其他元素上面有disabled相同的效果,我们需要使用pointer-events: none;
来实现。
css
.disabled {
color: rgba(0, 0, 0, 0.25);
cursor: not-allowed;
pointer-events: none;
}
如何选中闭合的shadowRoot
如果不了解原生的自定义组件开发,请看这里
我们知道dom.shadowRoot是一个 DOM 子树的根节点,它与文档的主 DOM 树分开渲染。所以我们如果想要获取到内部元素,我们就必须先获取到自定义元素的shadowRoot,就和iframe一样的。但是dom.shadowRoo只能选中开放的shadowDOM,即dom.attachShadow({ mode: "open" });
。闭合的shadowDOM我们我们是不能进行获取的。但是如果你是做浏览器扩展插件开发的。我们是可以通过chrome.dom.openOrClosedShadowRoot(element)
来获取。
如果我们想要方便选择给定的选择器元素,包括自定义组件中的元素的话,这里推荐一个库 query-selector-shadow-dom。以前也是基于这个库解决了项目中的bug,具体可以看这里
git如何移除已上传的文件
如果我们将不该上传的文件上传到git仓库中,我们如何把它删除呢?下面来介绍一下
在网上看有很多让使用git rm -r --cached .
,然后向.gitignore
中添加改文件即可,这个命令一执行,那么将会导致整个仓库文件都从git暂缓区移除。然后我们在提交,那么这些文件的提交记录都成了自己的了。
如果这样做了,那么我们应该如何回退呢。首先查看git log,使用git reset --hard commitid
回退到删除文件之前的代码,然后我们将代码强制push(因为我们已经将全部代码push了,所以当前回退后和远程仓库有冲突,所以需要先pull,如果不想pull,那么就需要强制push)。即git push -f
。
正确的做法是我们只将要移除的文件从暂缓区中移除就可以了。git rm -r --cached 指定文件
,然后在.gitignore
中添加该文件路径即可。然后将记录push即可。
数组转树
一般在开发中,我们会根据后端返回回来的数据进行分析处理成我们想要的数据结构。其中一种就是如何将后端返回的数组结构转化为树形结构,例如生成我们的文档菜单等等。
一般给的数据都会包括父级元素的标识,例如parentId
属性等。
- 找出根节点的id,一般设置为
""
。 - 遍历数组,找到父级元素标识和当前迭代的标识一致的元素,那么该元素将是当前标识的子元素。
- 每个元素都有作为根节点的机会。
- 迭代加递归。
js
const onlyRootFlatterData = [
{ id: 1, title: 'root1', parentId: "", childrenIds: [ 2, 4 ] },
{ id: 2, title: 'root1-parent1', parentId: 1, childrenIds: [ 3 ] },
{
id: 3,
title: 'root1-parent1-child1',
parentId: 2,
childrenIds: []
},
{
id: 4,
title: 'root1-parent2',
parentId: 1,
childrenIds: [ 5, 6, 7 ]
},
{
id: 5,
title: 'root1-parent2-child1',
parentId: 4,
childrenIds: []
},
{
id: 6,
title: 'root1-parent2-child2',
parentId: 4,
childrenIds: []
},
{
id: 7,
title: 'root1-parent2-child3',
parentId: 4,
childrenIds: []
}
]
// 根据parentId去和id比较,然后加入到对应的children
const result = [];
/**
* 每个元素都会有一个作为根节点的机会
*
* source 迭代数组
* result 树形结构
* parentId 当前根元素id
*/
const solve = (source, result, parentId) => {
for (let item of source) {
// 当前元素parentId是否和根节点id相同
if (item.parentId === parentId) {
let childrenItem = {
id: item.id,
title: item.title,
parentId: item.parentId,
children: []
}
result.push(childrenItem);
// 递归调用。将找到位置的元素作为根节点递归。
solve(source, childrenItem.children, item.id);
}
}
};
solve(onlyRootFlatterData, result, ""); // 顶层id为0,""
console.log("result", result)
树转数组
其实在日常开发中后端返回的数据为树形结构比较少,暂时没遇见过后台自己想要返回树形结构数据的。
- 可能树结构有多个根,我们遍历数组树结构。
- 递归函数内部,我们需要先将当前根元素加入到数组返回值中,然后再进行是否有children属性判断来决定是否处理到当前树的叶子元素。
js
const rootData = [
{
id: 1,
title: 'root1',
parentId: '',
children: [
{
id: 2,
title: 'root1-parent1',
parentId: 1,
children: [
{
id: 3,
title: 'root1-parent1-child1',
parentId: 2,
children: []
}
]
},
{
id: 4,
title: 'root1-parent2',
parentId: 1,
children: [
{
id: 5,
title: 'root1-parent2-child1',
parentId: 4,
children: []
},
{
id: 6,
title: 'root1-parent2-child2',
parentId: 4,
children: []
},
{
id: 7,
title: 'root1-parent2-child3',
parentId: 4,
children: []
}
]
}
]
}
]
const result = []
function solve(result, root) {
result.push({
id: root.id,
title: root.title,
childrenIds: root?.children?.map(item => item.id) || []
})
if(root?.children?.length > 0) {
// 循环,将当前内容加入到数组,然后递归
root?.children.forEach(item => {
solve(result, item)
})
}
}
for(let item of rootData) { // 多个根
solve(result, item)
}
console.log("result", result)
巧用Promise
因为有回调函数的存在,我们在开发中可能会造成回调地狱,导致代码看起来非常臃肿且不易维护。例如我们在开发chromev2插件时,由于chrome插件内部的api大多都是回调函数的形式返回结果。所以我们就需要使用Promise对其进行封装。
js
cb2Async<Params extends any[] = any[], Data = any>(fn: Function) {
// 返回一个函数
return (...params: Params): Promise<Data> => {
// 返回一个promise,让我们可以使用await进行同步写法。resolve直接作为传入api的回调函数。
return new Promise((resolve) => {
fn(...params, resolve);
});
};
}
js
// 获取当前窗口
const tabsQuery = cb2Async(chrome.tabs.query);
const tabs = await tabsQuery({ active: true, currentWindow: true });
window.currentTab = tabs && tabs[0]
往期年度总结
往期文章
- 众多跨标签页通信方式,你知道哪些?(二)
- 众多跨标签页通信方式,你知道哪些?
- 反调试吗?如何监听devtools的打开与关闭
- 因为原生,选择一家公司(前端如何防笔试作弊)
- 结合开发,带你熟悉package.json与tsconfig.json配置
- 如何优雅的在项目中使用echarts
- 如何优雅的做项目国际化
- 近三个月的排错,原来的憧憬消失喽
- 带你从0开始了解vue3核心(运行时)
- 带你从0开始了解vue3核心(computed, watch)
- 带你从0开始了解vue3核心(响应式)
- 3w+字的后台管理通用功能解决方案送给你
- 入职之前,狂补技术,4w字的前端技术解决方案送给你(vue3 + vite )
专栏文章
结语
本篇文章到此就结束了,欢迎在评论区交流。
🔥如果此文对你有帮助的话,欢迎💗关注 、👍点赞 、⭐收藏 、✍️评论, 支持一下博主~