<tree>
<tree-note></tree-note>
<tree-note name="这里是非默认名称"></tree-note>
<tree-note name="<input type='checkbox' id='myCheckbox' style='accent-color: #4CAF50;' checked><label for='myCheckbox' style='display: inline-block; margin-left: -3px;'>这里是标签</label>"></tree-note>
<tree-note status='open' name="这里是默认展开">
<tree-note status='close' name="这里是默认关闭">
<tree-note></tree-note>
<tree-note></tree-note>
</tree-note>
<tree-note name="这里是绿色线" line-color="#0f0">
<tree-note></tree-note>
<tree-note></tree-note>
</tree-note>
<tree-note name="这里是红色字" font-color="red">
<tree-note></tree-note>
<tree-note></tree-note>
</tree-note>
<tree-note name="这里是鼠标悬浮样式更改" hover-font-color="#19f" hover-background-color="#f91">
<tree-note></tree-note>
<tree-note></tree-note>
</tree-note>
<tree-note name="这里是鼠标焦点样式更改,点击后移开鼠标即可查看" focus-font-color="#08f" focus-background-color="#f80">
<tree-note></tree-note>
<tree-note></tree-note>
</tree-note>
</tree-note>
<tree-note>
<input slot="item" type="text" id="username" name="username" placeholder="这里是具名插槽" style="margin-left: 2px;height: 19px;font-weight: bold;">
</tree-note>
<tree-note>
<select slot="item" style="margin-left: 2px;height: 19px;font-weight: bold;" onclick="event.stopPropagation();//制止冒泡到上级触发点击事件">
<option value="option1">这里是含子项的具名插槽_且已阻止选择的点击事件冒泡到父级触发节点展开缩放</option>
<option value="option2">选项 2</option>
<option value="option3">选项 3</option>
<option value="option4">选项 4</option>
</select>
<tree-note></tree-note>
<tree-note></tree-note>
</tree-note>
<tree-note style="--tree-node-color-line:#0ff;" name="这里是直接style指定线条颜色">
<tree-note style="--tree-node-color-font:blue;" name="这里是直接style指定字体颜色">
<tree-note style="--tree-node-color-font:green;--tree-node-focus-background-color:#f00f00; --tree-node-focus-font-color:#0ff0ff;" name="这里是直接style指定焦点颜色">
<tree-note></tree-note>
<tree-note></tree-note>
</tree-note>
<tree-note style="--tree-node-color-line:green;--tree-node-hover-background-color:#f00fff; --tree-node-hover-font-color:#0ff000;" name="这里是直接style指定鼠标悬浮颜色">
<tree-note></tree-note>
<tree-note></tree-note>
</tree-note>
<tree-note style="--font-weight:bold;" name="<span style='font-weight: var(--font-weight);'>这里是指定标签统一子项字体粗细<span>">
<tree-note name="<span style='font-weight: var(--font-weight);'>粗字体<span>"></tree-note>
<tree-note name="<span style='font-weight: var(--font-weight);'>粗字体<span>"></tree-note>
</tree-note>
</tree-note>
<tree-note></tree-note>
</tree-note>
</tree>
<script>
// 上下文::host用于自定义元素的Shadow DOM中,而:root用于整个文档的根元素。
// 用途::host用于样式化自定义元素本身,而:root用于定义全局样式和变量。
// 作用域::host的样式仅限于自定义元素的Shadow DOM,:root的样式应用于整个文档。
// 不考虑对线条粗细,图标大小甚至内容的封装,没想明白咋样封装后在外部自定义合适
var treestyle =`
<style>
/*匹配根元素的自定义属性*//*必须小写字母以及--开头*/
:host {
/*--tree-node-color-line:#000;*//*定义树形节点线的颜色*/ /*注释掉,方便继承父项*/
/*--tree-node-color-font:#000;*//*定义树形节点线的颜色*/ /*注释掉,方便继承父项*/
/*--tree-node-hover-background-color:#555;*//*定义树形节点的鼠标悬浮背景色*/ /*注释掉,方便继承父项*/
/*--tree-node-focus-background-color:#999;*//*定义树形节点的获取焦点背景色*/ /*注释掉,方便继承父项*/
/*--tree-node-hover-font-color:#fff;*//*定义树形节点的鼠标悬浮字体色*/ /*注释掉,方便继承父项*/
/*--tree-node-focus-font-color:#fff*//*定义树形节点的获取焦点字体色*/ /*注释掉,方便继承父项*/
--tree-node-solid-line:linear-gradient(var(--tree-node-color-line,#000), var(--tree-node-color-line,#000));/*定义树形节点使用的实线*/
--tree-node-dotted-line:repeating-linear-gradient(var(--tree-node-color-line,#000) 0 4px,transparent 4px 8px);/*定义树形节点使用的虚线普通*/
--tree-node-dotted-lineX:linear-gradient(to right,transparent 0 9px,var(--tree-node-color-line,#000) 9px 13px,transparent 13px 17px,var(--tree-node-color-line,#000) 17px 21px);/*定义树形节点使用的虚线横向截半*/
--tree-node-dotted-lineY:linear-gradient(to top,transparent 0 9px,var(--tree-node-color-line,#000) 9px 13px,transparent 13px 17px,var(--tree-node-color-line,#000) 17px 21px);/*定义树形节点使用的虚线竖向截半*/
}
/*--------------------------------------------------------------*/
.hidden{
display: none !important;
}
.tree_item::before{
content: ''; /* 这里可以设置为一个空格 ' ',以确保伪元素有内容可以显示背景 */
width: 19px;
height: 19px;
box-sizing: border-box;/*元素的宽度和高度会包括内容、内边距和边框,但不包括外边距。*/
flex-shrink: 0;/*0:空间不足不会缩放元素*/
}
.tree_item:has(+.tree_children>slot[data-slot-childinfo="not empty"])::before{
cursor: pointer;/*鼠标样式一只手*/
}
.tree_item {
display:flex !important;
width:100%;
height:19px !important;
font-size:14px;
user-select:none;/*不会被选中*/
white-space:nowrap;/*不会换行*/
color:var(--tree-node-color-font,#000);/*字的颜色*/
text-indent: 2px;/*字体首行缩进*/
align-items:center;/*内部元素垂直居中排列--此处用于文字垂直居中*/
box-sizing: border-box;/*元素的宽度和高度会包括内容、内边距和边框,但不包括外边距。----避免设置边距导致元素变大*/
flex-shrink: 0;/*0:空间不足不会缩放元素*/
}
/*在前面保证能被hover的样式覆盖*/
.tree_item:focus{
background:linear-gradient(var(--tree-node-focus-background-color,#999), var(--tree-node-focus-background-color,#999)) 20px 50%/100% 100% no-repeat;/*麻烦_不方便搞圆角*/
color:var(--tree-node-focus-font-color,#fff);/*字颜色*/
}
.tree_item:hover{
background:linear-gradient(var(--tree-node-hover-background-color,#555), var(--tree-node-hover-background-color,#555)) 20px 50%/100% 100% no-repeat;/*麻烦_不方便搞圆角*/
color:var(--tree-node-hover-font-color,#fff);/*字颜色*/
}
.tree_children {
width:100%;
padding:0 0 0 19px;
box-sizing: border-box;/*元素的宽度和高度会包括内容、内边距和边框,但不包括外边距。----避免设置边距导致元素变大*/
flex-shrink: 0;/*0:空间不足不会缩放元素*/
}
/*不是最后一个*/
:host(:not(:is(:last-child)))>.tree_children { /*.tree_node必须存在父元素,不能是根元素,不然不生效*/
background:var(--tree-node-dotted-line) 9px 0%/1px 100% repeat-y;
}
/*没有子元素且不是最后一个*/
:host(:not(:is(:last-child)))>.tree_item:has(+.tree_children>slot[data-slot-childinfo="empty"])::before{ /*莫名:empty无效?*/
background:var(--tree-node-dotted-line) 50%/1px 100% repeat-y,
var(--tree-node-dotted-lineX) 0px/100% 1px repeat-x;
}
/*没有子元素且是最后一个*/
:host(:is(:last-child))>.tree_item:has(+.tree_children>slot[data-slot-childinfo="empty"])::before{ /*莫名:empty无效?*/
background:var(--tree-node-dotted-lineY) 50%/1px 100% repeat-y,
var(--tree-node-dotted-lineX) 0px/100% 1px repeat-x;
}
/*有子元素没隐藏且不是最后一个*/
:host(:not(:is(:last-child)))>.tree_item:has(+.tree_children:not(.hidden)>slot[data-slot-childinfo="not empty"])::before{
background: /*var(--tree-node-solid-line) 50%/1px 5px no-repeat,*//*中竖*/
var(--tree-node-solid-line) 50%/5px 1px no-repeat,/*中横*/
var(--tree-node-solid-line) 50% top/1px 2px no-repeat,/*上竖*/
var(--tree-node-solid-line) 50% bottom/1px 2px no-repeat,/*下竖*/
var(--tree-node-solid-line) right/2px 1px no-repeat,/*右横*/
/*var(--tree-node-solid-line) left/2px 1px no-repeat,*//*左横-用不到*/
var(--tree-node-solid-line) 4px/1px 11px no-repeat,/*左框*/
var(--tree-node-solid-line) 14px/1px 11px no-repeat,/*右框*/
var(--tree-node-solid-line) center 4px/11px 1px no-repeat,/*上框*/
var(--tree-node-solid-line) center 14px/11px 1px no-repeat;/*下框*/
}
/*有子元素没隐藏且是最后一个*/
:host(:is(:last-child))>.tree_item:has(+.tree_children:not(.hidden)>slot[data-slot-childinfo="not empty"])::before{
background: /*var(--tree-node-solid-line) 50%/1px 5px no-repeat,*//*中竖*/
var(--tree-node-solid-line) 50%/5px 1px no-repeat,/*中横*/
var(--tree-node-solid-line) 50% top/1px 2px no-repeat,/*上竖*/
/*var(--tree-node-solid-line) 50% bottom/1px 2px no-repeat,*//*下竖*/
var(--tree-node-solid-line) right/2px 1px no-repeat,/*右横*/
/*var(--tree-node-solid-line) left/2px 1px no-repeat,*//*左横-用不到*/
var(--tree-node-solid-line) 4px/1px 11px no-repeat,/*左框*/
var(--tree-node-solid-line) 14px/1px 11px no-repeat,/*右框*/
var(--tree-node-solid-line) center 4px/11px 1px no-repeat,/*上框*/
var(--tree-node-solid-line) center 14px/11px 1px no-repeat;/*下框*/
}
/*有子元素已隐藏且不是最后一个*/
:host(:not(:is(:last-child)))>.tree_item:has(+.tree_children.hidden>slot[data-slot-childinfo="not empty"])::before{
background: var(--tree-node-solid-line) 50%/1px 5px no-repeat,/*中竖*/
var(--tree-node-solid-line) 50%/5px 1px no-repeat,/*中横*/
var(--tree-node-solid-line) 50% top/1px 2px no-repeat,/*上竖*/
var(--tree-node-solid-line) 50% bottom/1px 2px no-repeat,/*下竖*/
var(--tree-node-solid-line) right/2px 1px no-repeat,/*右横*/
/*var(--tree-node-solid-line) left/2px 1px no-repeat,*//*左横-用不到*/
var(--tree-node-solid-line) 4px/1px 11px no-repeat,/*左框*/
var(--tree-node-solid-line) 14px/1px 11px no-repeat,/*右框*/
var(--tree-node-solid-line) center 4px/11px 1px no-repeat,/*上框*/
var(--tree-node-solid-line) center 14px/11px 1px no-repeat;/*下框*/
}
/*有子元素已隐藏且是最后一个*/
:host(:is(:last-child))>.tree_item:has(+.tree_children.hidden>slot[data-slot-childinfo="not empty"])::before{ /*莫名:empty无效?*/
background: var(--tree-node-solid-line) 50%/1px 5px no-repeat,/*中竖*/
var(--tree-node-solid-line) 50%/5px 1px no-repeat,/*中横*/
var(--tree-node-solid-line) 50% top/1px 2px no-repeat,/*上竖*/
/*var(--tree-node-solid-line) 50% bottom/1px 2px no-repeat,*//*下竖*/
var(--tree-node-solid-line) right/2px 1px no-repeat,/*右横*/
/*var(--tree-node-solid-line) left/2px 1px no-repeat,*//*左横-用不到*/
var(--tree-node-solid-line) 4px/1px 11px no-repeat,/*左框*/
var(--tree-node-solid-line) 14px/1px 11px no-repeat,/*右框*/
var(--tree-node-solid-line) center 4px/11px 1px no-repeat,/*上框*/
var(--tree-node-solid-line) center 14px/11px 1px no-repeat;/*下框*/
}
</style>
`
class TreeNote extends HTMLElement {
constructor() { //默认构造函数
super();
this.attachShadow({ mode: 'open' })
this.shadowRoot.innerHTML = `
${treestyle}
<!--TreeNote-->
<div class="tree_item" onclick="var _children=this.nextElementSibling;if(_children.firstChild.getAttribute('data-slot-childinfo')==='not empty' && event.clientX<this.getBoundingClientRect().left+19){_children.classList.toggle('hidden');if(_children.classList.contains('hidden')){_children.getRootNode().host.setAttribute('status','close');}else{_children.getRootNode().host.setAttribute('status','open');}}" tabindex="0"><slot name="item">默认名称</slot></div><!--getRootNode().host可以获取到自定义元素本身-->
<div class="tree_children"><slot onslotchange="const assignedNodes =this.assignedNodes({ flatten: false});const status = this.getAttribute('data-slot-childinfo');const hasChild = assignedNodes.length > 0 && assignedNodes.some(node => { return node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE && node.nodeValue.trim() !== ''});if(status!=='not empty' && hasChild){this.setAttribute('data-slot-childinfo', 'not empty');} if(status!=='empty' && !hasChild){this.setAttribute('data-slot-childinfo', 'empty');}" data-slot-childinfo="empty"></slot></div><!--若外部子元素为空,则onslotchange事件不会触发--><!--状态判断反向方式限定只能是那两个词,不考虑大写,以及不判断后期移除子项后清除状态属性,保留一下状态-->
`;
}
connectedCallback() { // 当元素被插入到文档时被调用
}
disconnectedCallback() { // 当元素从文档中删除时被调用
}
adoptedCallback() { // 元素被移动到新的文档或框架中的逻辑
}
attributeChangedCallback(name, oldValue, newValue) { // 当元素的属性被添加、删除或修改时被调用
const tree_item = this.shadowRoot.querySelector('.tree_item');
const tree_children = this.shadowRoot.querySelector('.tree_children');
switch (name) {
case 'name': // 处理name属性的逻辑
tree_item.firstChild.innerHTML=newValue;
break;
case 'status': // 处理status属性的逻辑
if(newValue=="open" && tree_children.classList.contains('hidden')){tree_children.classList.remove('hidden')}
if(newValue=="close" && !tree_children.classList.contains('hidden')){tree_children.classList.add('hidden')}
break;
case 'line-color': // 处理line-color属性的逻辑
this.style.setProperty('--tree-node-color-line', newValue);
break;
case 'font-color': // 处理font-color属性的逻辑
this.style.setProperty('--tree-node-color-font', newValue);
break;
case 'hover-font-color': // 处理hover-font-color属性的逻辑
this.style.setProperty('--tree-node-hover-font-color', newValue);
break;
case 'hover-background-color': // 处理hover-background-color属性的逻辑
this.style.setProperty('--tree-node-hover-background-color', newValue);
break;
case 'focus-font-color': // 处理focus-font-color属性的逻辑
this.style.setProperty('--tree-node-focus-font-color', newValue);
break;
case 'focus-background-color': // 处理focus-background-color属性的逻辑
this.style.setProperty('--tree-node-focus-background-color', newValue);
break;
default:
break;
}
}
static get observedAttributes() { // 定义需要监听的属性
return ["name","status","line-color","font-color","hover-font-color","hover-background-color","focus-font-color","focus-background-color"];
}
}
customElements.define('tree-note', TreeNote);//必须自定义元素名小写
</script>