Skip to content

Commit 46e49a9

Browse files
committed
fix: 修复选中段落设置行高,最后一行的内容会被重复添加的问题
1 parent 38b4b22 commit 46e49a9

File tree

2 files changed

+71
-150
lines changed

2 files changed

+71
-150
lines changed

src/menus/lineHeight/index.ts

+57-144
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import $, { DomElement } from '../../utils/dom-core'
99
import Editor from '../../editor/index'
1010
import { MenuActive } from '../menu-constructors/Menu'
1111
import lineHeightList from './lineHeightList'
12-
import { UA } from '../../utils/util'
1312

1413
class LineHeight extends DropListMenu implements MenuActive {
1514
constructor(editor: Editor) {
@@ -39,163 +38,81 @@ class LineHeight extends DropListMenu implements MenuActive {
3938
* @param value value
4039
*/
4140
public command(value: string): void {
42-
let selection = window.getSelection ? window.getSelection() : document.getSelection()
43-
//允许设置dom
44-
const allowArray: string[] = ['P']
4541
const editor = this.editor
46-
let st: string = ''
47-
//恢复焦点
42+
43+
//重置选区
4844
editor.selection.restoreSelection()
49-
const $selectionElem = $(editor.selection.getSelectionContainerElem())
5045

51-
if (!$selectionElem?.length) return
46+
// 获取选区的祖先元素
47+
const $containerElem = $(editor.selection.getSelectionContainerElem())
5248

53-
const $selectionAll = $(editor.selection.getSelectionContainerElem())
54-
// let dom:HTMLElement= $selectionElem.elems[0]
55-
let dom: HTMLElement = $(editor.selection.getSelectionStartElem()).elems[0]
56-
//获取元素的style
57-
let style: string | null = ''
58-
let styleList: string[] = []
59-
//点击默认的时候删除line-height属性 并重新设置 style
60-
let styleStr: string = ''
49+
if (!$containerElem.elems.length) return
6150

6251
//选中多行操作
63-
if ($selectionElem && editor.$textElem.equal($selectionElem)) {
64-
let isIE = UA.isIE()
65-
//获取range 开头结束的dom在 祖父元素的下标
66-
let indexStore: Array<number> = []
67-
let arrayDom_a: Array<HTMLElement> = []
68-
let arrayDom_b: Array<HTMLElement> = []
52+
if ($containerElem && editor.$textElem.equal($containerElem)) {
53+
// 标识是否可以设置行高的样式
54+
let setStyleLock: boolean = false
55+
6956
//获取range 开头结束的dom
70-
const StartElem: DomElement = $(editor.selection.getSelectionStartElem())
71-
const EndElem: DomElement = $(editor.selection.getSelectionEndElem())
72-
const childList: NodeListOf<ChildNode> | undefined = editor.selection.getRange()
73-
?.commonAncestorContainer.childNodes
74-
arrayDom_a.push(this.getDom(StartElem.elems[0]))
75-
childList?.forEach((item, index) => {
76-
if (item === this.getDom(StartElem.elems[0])) {
77-
indexStore.push(index)
78-
}
79-
if (item === this.getDom(EndElem.elems[0])) {
80-
indexStore.push(index)
81-
}
82-
})
83-
//遍历 获取头尾之间的dom元素
84-
let i = 0
85-
let d: HTMLElement
86-
arrayDom_b.push(this.getDom(StartElem.elems[0]))
87-
while (arrayDom_a[i] !== this.getDom(EndElem.elems[0])) {
88-
d = $(arrayDom_a[i].nextElementSibling).elems[0]
89-
if (allowArray.indexOf($(d).getNodeName()) !== -1) {
90-
arrayDom_b.push(d)
91-
arrayDom_a.push(d)
92-
} else {
93-
arrayDom_a.push(d)
94-
}
95-
i++
96-
}
57+
const selectionStartElem: HTMLElement = $(editor.selection.getSelectionStartElem())
58+
.elems[0]
59+
const SelectionEndElem: HTMLElement = $(editor.selection.getSelectionEndElem()).elems[0]
9760

98-
//设置段落选取 全选
99-
if ($(arrayDom_a[0]).getNodeName() !== 'P') {
100-
i = 0
101-
//遍历集合得到第一个p标签的下标
102-
for (var k = 0; k < arrayDom_a.length; k++) {
103-
if ($(arrayDom_a[k]).getNodeName() === 'P') {
104-
i = k
105-
break
106-
}
107-
}
108-
//i===0 说明选区中没有p段落
109-
if (i === 0) {
110-
return
111-
}
112-
let _i = 0
113-
while (_i !== i) {
114-
arrayDom_a.shift()
115-
_i++
61+
// 获取选区中,在contenteditable下的直接父元素
62+
const StartElemWrap: HTMLElement = this.getDom(selectionStartElem)
63+
const EndElemWrap: HTMLElement = this.getDom(SelectionEndElem)
64+
65+
const containerElemChildren = $containerElem.elems[0].children
66+
67+
for (let i = 0; i < containerElemChildren.length; i++) {
68+
const item: HTMLElement = containerElemChildren[i] as HTMLElement
69+
70+
// 目前只支持p 段落标签设置行高
71+
if ($(item).getNodeName() !== 'P') {
72+
continue
11673
}
117-
}
118-
//设置替换的选区
119-
this.setRange(arrayDom_a[0], arrayDom_a[arrayDom_a.length - 1])
120-
//生成innerHtml html字符串
121-
arrayDom_a.forEach(item => {
122-
style = item.getAttribute('style')
123-
styleList = style ? style.split(';') : []
124-
styleStr = this.styleProcessing(styleList)
125-
126-
if ($(item).getNodeName() === 'P') {
127-
//判断是否 点击默认
128-
if (value) {
129-
styleStr += value ? `line-height:${value};` : ''
130-
}
74+
75+
if (item === StartElemWrap) {
76+
setStyleLock = true
13177
}
13278

133-
if (!isIE) {
134-
st += `<${$(item).getNodeName().toLowerCase()} style="${styleStr}">${
135-
item.innerHTML
136-
}</${$(item).getNodeName().toLowerCase()}>`
137-
} else {
79+
// 证明在区间节点里
80+
if (setStyleLock) {
13881
$(item).css('line-height', value)
139-
}
140-
})
14182

142-
if (st) {
143-
this.action(st, editor)
83+
if (item === EndElemWrap) {
84+
setStyleLock = false
85+
86+
// 当设置完选择的EndElemWrap时,就可以退出
87+
return
88+
}
89+
}
14490
}
14591

146-
//恢复已选择的选区
147-
dom = $selectionAll.elems[0]
148-
this.setRange(dom.children[indexStore[0]], dom.children[indexStore[1]])
92+
//重新设置选区
93+
editor.selection.createRangeByElems(selectionStartElem, SelectionEndElem)
94+
14995
return
15096
}
15197

152-
//遍历dom 获取祖父元素 直到contenteditable属性的div标签
153-
dom = this.getDom(dom)
98+
// 单行操作
99+
// 选中区间的dom元素
100+
const selectElem = $containerElem.elems[0]
154101

155-
//校验允许lineheight设置标签
156-
if (allowArray.indexOf($(dom).getNodeName()) === -1) {
157-
return
158-
}
159-
style = dom.getAttribute('style')
160-
styleList = style ? style.split(';') : []
161-
//全选 dom下所有的内容
162-
selection?.selectAllChildren(dom)
163-
//保存range
164-
editor.selection.saveRange()
165-
//判断是否存在value 默认 移除line-height
166-
if (!value) {
167-
if (style) {
168-
styleStr = this.styleProcessing(styleList)
169-
//避免没有其它属性 只留下 ‘style’ 减少代码
170-
if (styleStr === '') {
171-
st = `<${$(dom).getNodeName().toLowerCase()}>${dom.innerHTML}</${$(dom)
172-
.getNodeName()
173-
.toLowerCase()}>`
174-
} else {
175-
st = `<${$(dom).getNodeName().toLowerCase()} style="${styleStr}">${
176-
dom.innerHTML
177-
}</${$(dom).getNodeName().toLowerCase()}>`
178-
}
179-
this.action(st, editor)
180-
}
102+
// 获取选区中,在contenteditable下的直接父元素
103+
const selectElemWrapdom = this.getDom(selectElem)
104+
105+
// 目前只支持p 段落标签设置行高
106+
if ($(selectElemWrapdom).getNodeName() !== 'P') {
181107
return
182108
}
183-
if (style) {
184-
//存在style 检索其它style属性
185-
styleStr = this.styleProcessing(styleList) + `line-height:${value};`
186-
} else {
187-
styleStr = `line-height:${value};`
188-
}
189-
st = `<${$(dom).getNodeName().toLowerCase()} style="${styleStr}">${dom.innerHTML}</${$(dom)
190-
.getNodeName()
191-
.toLowerCase()}>`
192109

193-
//防止BLOCKQUOTE叠加 or IE下导致P嵌套出现误删
194-
if ($(dom).getNodeName() === 'BLOCKQUOTE' || UA.isIE()) {
195-
$(dom).css('line-height', value)
196-
} else {
197-
this.action(st, editor)
198-
}
110+
$(selectElemWrapdom).css('line-height', value)
111+
112+
//重新设置选区
113+
editor.selection.createRangeByElems(selectElemWrapdom, selectElemWrapdom)
114+
115+
return
199116
}
200117

201118
/**
@@ -220,16 +137,10 @@ class LineHeight extends DropListMenu implements MenuActive {
220137
return DOM
221138
}
222139

223-
/**
224-
* 执行 document.execCommand
225-
*
226-
*/
227-
public action(html_str: string, editor: Editor): void {
228-
editor.cmd.do('insertHTML', html_str)
229-
}
230-
231140
/**
232141
* style 处理
142+
*
143+
* 废弃的方法
233144
*/
234145
public styleProcessing(styleList: Array<string>): string {
235146
let styleStr = ''
@@ -243,6 +154,8 @@ class LineHeight extends DropListMenu implements MenuActive {
243154

244155
/**
245156
* 段落全选 比如:避免11变成111
157+
*
158+
* 废弃的方法
246159
*/
247160
public setRange(startDom: Node, endDom: Node): void {
248161
const editor = this.editor

test/unit/menus/lineHeight.test.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,13 @@ describe('LineHeight menu', () => {
3232
mockCmdFn(document)
3333
const cmdVal = '2'
3434
lineHeightMenu.command(cmdVal)
35-
// 此处触发 editor.cmd.do('insertHTML', xx),可以被 jest 成功执行,具体参考 mockCmdFn 的描述
36-
expect(editor.$textElem.elems[0]).toContainHTML('<p style="line-height:2;"><br></p>')
35+
36+
// 空状态下,设置行高
37+
expect(
38+
editor.$textElem.elems[0].innerHTML.indexOf(
39+
'<p data-we-empty-p="" style="line-height:2;"><br></p>'
40+
)
41+
).toBeGreaterThanOrEqual(0)
3742
})
3843

3944
test('lineHeight 菜单:选择多行增加行高', () => {
@@ -99,7 +104,8 @@ describe('LineHeight menu', () => {
99104

100105
const cmdVal = '2'
101106
lineHeightMenu.command(cmdVal)
102-
// 此处触发 editor.cmd.do('insertHTML', xx),可以被 jest 成功执行,具体参考 mockCmdFn 的描述
107+
108+
// 设置多行时,只针对p, 段落标签其设置
103109
expect(
104110
editor.$textElem.elems[0].innerHTML.indexOf(
105111
'<div>345</div><div>234<span>123</span></div>'
@@ -110,15 +116,16 @@ describe('LineHeight menu', () => {
110116
test('lineHeight 菜单:增加行高, 如果不传value值,则设为默认行高,并且不设置 line-height 样式', () => {
111117
mockCmdFn(document)
112118

113-
editor.txt.html('<p style="color:red;">123</p>')
119+
editor.txt.html('<p style="color:red">123</p>')
114120

115121
const [startNode] = Array.from(editor.$textElem.elems[0].childNodes)
116122
lineHeightMenu.setRange(startNode, startNode)
117123

118124
lineHeightMenu.command('')
119125
// 此处触发 editor.cmd.do('insertHTML', xx),可以被 jest 成功执行,具体参考 mockCmdFn 的描述
126+
120127
expect(
121-
editor.$textElem.elems[0].innerHTML.indexOf('<p style="color:red;">123</p>')
128+
editor.$textElem.elems[0].innerHTML.indexOf('<p style="color:red">123</p>')
122129
).toBeGreaterThanOrEqual(0)
123130
})
124131

@@ -132,9 +139,10 @@ describe('LineHeight menu', () => {
132139

133140
lineHeightMenu.command('2')
134141
// 此处触发 editor.cmd.do('insertHTML', xx),可以被 jest 成功执行,具体参考 mockCmdFn 的描述
142+
135143
expect(
136144
editor.$textElem.elems[0].innerHTML.indexOf(
137-
'<p style="color:red;line-height:2;">123</p>'
145+
'<p style="color:red; line-height:2;">123</p>'
138146
)
139147
).toBeGreaterThanOrEqual(0)
140148
})

0 commit comments

Comments
 (0)