lk-text-select-highlight 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +56 -0
- package/LICENSE +21 -0
- package/README-cn.md +128 -0
- package/README.md +78 -0
- package/dist/lk-text-select-highlight.cjs.js +182 -0
- package/dist/lk-text-select-highlight.cjs.js.map +1 -0
- package/dist/lk-text-select-highlight.esm.js +180 -0
- package/dist/lk-text-select-highlight.esm.js.map +1 -0
- package/dist/lk-text-select-highlight.min.js +2 -0
- package/dist/lk-text-select-highlight.min.js.map +1 -0
- package/dist/lk-text-select-highlight.umd.js +188 -0
- package/dist/lk-text-select-highlight.umd.js.map +1 -0
- package/index.js +186 -0
- package/package.json +51 -0
- package/src/index.js +179 -0
- package/types/index.d.ts +89 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# lk-text-select-highlight - Project Structure
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
A lightweight JavaScript library for highlighting selected text on web pages.
|
|
5
|
+
|
|
6
|
+
## File Structure
|
|
7
|
+
```
|
|
8
|
+
lk-text-select-highlight/
|
|
9
|
+
├── src/
|
|
10
|
+
│ └── index.js # Main source code (ES6)
|
|
11
|
+
├── dist/ # Built distribution files
|
|
12
|
+
│ ├── lk-text-select-highlight.cjs.js # CommonJS version
|
|
13
|
+
│ ├── lk-text-select-highlight.cjs.js.map # Source map for CJS
|
|
14
|
+
│ ├── lk-text-select-highlight.esm.js # ES modules version
|
|
15
|
+
│ ├── lk-text-select-highlight.esm.js.map # Source map for ESM
|
|
16
|
+
│ ├── lk-text-select-highlight.umd.js # UMD version for browsers
|
|
17
|
+
│ ├── lk-text-select-highlight.umd.js.map # Source map for UMD
|
|
18
|
+
│ ├── lk-text-select-highlight.min.js # Minified UMD version
|
|
19
|
+
│ └── lk-text-select-highlight.min.js.map # Source map for minified
|
|
20
|
+
├── test/
|
|
21
|
+
│ └── index.test.js # Test file
|
|
22
|
+
├── demo.html # Demo/example HTML file
|
|
23
|
+
├── styles.css # Optional default styles
|
|
24
|
+
├── README.md # Project documentation
|
|
25
|
+
├── LICENSE # MIT License
|
|
26
|
+
├── .npmignore # Files to exclude from npm package
|
|
27
|
+
├── package.json # Package manifest
|
|
28
|
+
├── rollup.config.cjs # Build configuration
|
|
29
|
+
└── index.js # Legacy entry point (backward compatibility)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
- Supports multiple module formats (CommonJS, ES Modules, UMD)
|
|
34
|
+
- Lightweight with no external dependencies
|
|
35
|
+
- Customizable highlight appearance
|
|
36
|
+
- Cross-browser compatibility
|
|
37
|
+
- Tree-shakeable exports
|
|
38
|
+
|
|
39
|
+
## Build Process
|
|
40
|
+
The library uses Rollup to generate multiple builds:
|
|
41
|
+
- `cjs`: For Node.js usage
|
|
42
|
+
- `esm`: For modern bundlers
|
|
43
|
+
- `umd`: For direct browser usage
|
|
44
|
+
- `min`: Minified version for production
|
|
45
|
+
|
|
46
|
+
## Publishing
|
|
47
|
+
To publish to npm:
|
|
48
|
+
1. Update version in package.json
|
|
49
|
+
2. Run tests: `npm test`
|
|
50
|
+
3. Build: `npm run build`
|
|
51
|
+
4. Publish: `npm publish`
|
|
52
|
+
|
|
53
|
+
## Development
|
|
54
|
+
- Run in watch mode: `npm run dev`
|
|
55
|
+
- Run tests: `npm test`
|
|
56
|
+
- Build: `npm run build`
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Your Name
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README-cn.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# lk-text-select-highlight
|
|
2
|
+
|
|
3
|
+
一个轻量级的JavaScript库,用于在网页上高亮选中的文本。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install lk-text-select-highlight
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 使用方法
|
|
12
|
+
|
|
13
|
+
### ES6 模块导入
|
|
14
|
+
```javascript
|
|
15
|
+
import TextHighlighter from 'lk-text-select-highlight';
|
|
16
|
+
|
|
17
|
+
const highlighter = new TextHighlighter({
|
|
18
|
+
className: 'my-highlight',
|
|
19
|
+
color: '#ffeb3b'
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// 高亮选中的文本
|
|
23
|
+
document.addEventListener('mouseup', () => {
|
|
24
|
+
highlighter.highlightSelection();
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 浏览器脚本标签
|
|
29
|
+
```html
|
|
30
|
+
<script src="https://unpkg.com/lk-text-select-highlight/dist/lk-text-select-highlight.min.js"></script>
|
|
31
|
+
<script>
|
|
32
|
+
const highlighter = new TextHighlighter({
|
|
33
|
+
className: 'my-highlight',
|
|
34
|
+
color: '#ffeb3b'
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// 高亮选中的文本
|
|
38
|
+
document.addEventListener('mouseup', () => {
|
|
39
|
+
highlighter.highlightSelection();
|
|
40
|
+
});
|
|
41
|
+
</script>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## API
|
|
45
|
+
|
|
46
|
+
### new TextHighlighter(options)
|
|
47
|
+
|
|
48
|
+
使用以下选项初始化新的 TextHighlighter 实例:
|
|
49
|
+
|
|
50
|
+
- `className`: 高亮元素的CSS类名 (默认: 'highlighted-text')
|
|
51
|
+
- `color`: 高亮背景颜色 (默认: '#ffff00')
|
|
52
|
+
- `container`: 限制文本选择高亮的DOM元素 (默认: document.body)
|
|
53
|
+
- `strictMode`: 如果为true,则防止在包含目标className元素的选择上进行高亮 (默认: false)
|
|
54
|
+
- `caseSensitive`: 搜索是否区分大小写 (默认: true)
|
|
55
|
+
|
|
56
|
+
### highlighter.highlightSelection()
|
|
57
|
+
|
|
58
|
+
高亮页面上当前选中的文本。
|
|
59
|
+
|
|
60
|
+
### highlighter.removeAllHighlights()
|
|
61
|
+
|
|
62
|
+
移除所有现有的高亮。
|
|
63
|
+
|
|
64
|
+
### highlighter.removeHighlight(highlight)
|
|
65
|
+
|
|
66
|
+
移除指定的高亮对象。高亮对象可以从 getHighlights() 方法获取。
|
|
67
|
+
|
|
68
|
+
### highlighter.getHighlights()
|
|
69
|
+
|
|
70
|
+
返回所有当前高亮的数组。
|
|
71
|
+
|
|
72
|
+
## 配置选项详解
|
|
73
|
+
|
|
74
|
+
### container 选项
|
|
75
|
+
允许您限制文本选择高亮的范围。如果您只想在特定DOM元素内启用高亮功能,可以使用此选项:
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
const container = document.getElementById('content-area');
|
|
79
|
+
const highlighter = new TextHighlighter({
|
|
80
|
+
container: container
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### strictMode 选项
|
|
85
|
+
启用严格模式后,如果选择的文本范围内包含相同类名的元素,将不会进行高亮。这对于防止嵌套高亮非常有用:
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
const highlighter = new TextHighlighter({
|
|
89
|
+
className: 'highlighted-text',
|
|
90
|
+
strictMode: true // 启用严格模式
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 示例
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
// 基本用法
|
|
98
|
+
const highlighter = new TextHighlighter();
|
|
99
|
+
|
|
100
|
+
// 自定义选项
|
|
101
|
+
const customHighlighter = new TextHighlighter({
|
|
102
|
+
className: 'custom-highlight',
|
|
103
|
+
color: '#ff6b6b',
|
|
104
|
+
container: document.querySelector('.content'),
|
|
105
|
+
strictMode: true
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// 高亮选中的文本
|
|
109
|
+
document.addEventListener('mouseup', () => {
|
|
110
|
+
highlighter.highlightSelection();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// 获取所有高亮
|
|
114
|
+
const highlights = highlighter.getHighlights();
|
|
115
|
+
|
|
116
|
+
// 移除特定高亮
|
|
117
|
+
if (highlights.length > 0) {
|
|
118
|
+
highlighter.removeHighlight(highlights[0]);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## 浏览器兼容性
|
|
123
|
+
|
|
124
|
+
支持所有现代浏览器 (Chrome, Firefox, Safari, Edge)。
|
|
125
|
+
|
|
126
|
+
## 许可证
|
|
127
|
+
|
|
128
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# lk-text-select-highlight
|
|
2
|
+
|
|
3
|
+
A lightweight JavaScript library for highlighting selected text on web pages.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install lk-text-select-highlight
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### ES6 Import
|
|
14
|
+
```javascript
|
|
15
|
+
import TextHighlighter from 'lk-text-select-highlight';
|
|
16
|
+
|
|
17
|
+
const highlighter = new TextHighlighter({
|
|
18
|
+
className: 'my-highlight',
|
|
19
|
+
color: '#ffeb3b'
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Highlight selected text
|
|
23
|
+
document.addEventListener('mouseup', () => {
|
|
24
|
+
highlighter.highlightSelection();
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Browser Script Tag
|
|
29
|
+
```html
|
|
30
|
+
<script src="https://unpkg.com/lk-text-select-highlight/dist/lk-text-select-highlight.min.js"></script>
|
|
31
|
+
<script>
|
|
32
|
+
const highlighter = new TextHighlighter({
|
|
33
|
+
className: 'my-highlight',
|
|
34
|
+
color: '#ffeb3b'
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Highlight selected text
|
|
38
|
+
document.addEventListener('mouseup', () => {
|
|
39
|
+
highlighter.highlightSelection();
|
|
40
|
+
});
|
|
41
|
+
</script>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## API
|
|
45
|
+
|
|
46
|
+
### new TextHighlighter(options)
|
|
47
|
+
|
|
48
|
+
Initialize a new TextHighlighter instance with the following options:
|
|
49
|
+
|
|
50
|
+
- `className`: CSS class name for highlighted elements (default: 'highlighted-text')
|
|
51
|
+
- `color`: Background color for highlights (default: '#ffff00')
|
|
52
|
+
- `container`: DOM element that limits where text selection highlighting occurs (default: document.body)
|
|
53
|
+
- `strictMode`: If true, prevents highlighting selections that contain elements with the target className (default: false)
|
|
54
|
+
- `caseSensitive`: Whether search should be case sensitive (default: true)
|
|
55
|
+
|
|
56
|
+
### highlighter.highlightSelection()
|
|
57
|
+
|
|
58
|
+
Highlights the currently selected text on the page.
|
|
59
|
+
|
|
60
|
+
### highlighter.removeAllHighlights()
|
|
61
|
+
|
|
62
|
+
Removes all existing highlights.
|
|
63
|
+
|
|
64
|
+
### highlighter.getHighlights()
|
|
65
|
+
|
|
66
|
+
Returns an array of all current highlights.
|
|
67
|
+
|
|
68
|
+
### highlighter.removeHighlight(highlight)
|
|
69
|
+
|
|
70
|
+
Removes a specific highlight object. The highlight object can be obtained from the getHighlights() method.
|
|
71
|
+
|
|
72
|
+
### highlighter.removeAllHighlights()
|
|
73
|
+
|
|
74
|
+
Removes all existing highlights.
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
|
|
78
|
+
MIT
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* lk-text-select-highlight - A lightweight text selection highlight library
|
|
5
|
+
* @module lk-text-select-highlight
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
class TextHighlighter {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
this.options = {
|
|
11
|
+
className: options.className || "highlighted-text",
|
|
12
|
+
color: options.color || "#ffff00",
|
|
13
|
+
container: options.container || document.body, // DOM容器,默认为整个body
|
|
14
|
+
strictMode: options.strictMode || false, // 严格模式,默认为false
|
|
15
|
+
caseSensitive:
|
|
16
|
+
options.caseSensitive !== undefined
|
|
17
|
+
? options.caseSensitive
|
|
18
|
+
: true,
|
|
19
|
+
...options,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
this.highlights = [];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Highlight selected text
|
|
27
|
+
*/
|
|
28
|
+
highlightSelection() {
|
|
29
|
+
const selection = window.getSelection();
|
|
30
|
+
if (!selection.toString().trim()) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const range = selection.getRangeAt(0);
|
|
35
|
+
|
|
36
|
+
// 检查选择的范围是否在指定的容器内
|
|
37
|
+
const container = this.options.container;
|
|
38
|
+
if (
|
|
39
|
+
!container.contains(range.startContainer) ||
|
|
40
|
+
!container.contains(range.endContainer)
|
|
41
|
+
) {
|
|
42
|
+
return false; // 选择超出了指定容器范围
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 更全面地检查整个范围是否在容器内
|
|
46
|
+
const commonAncestor = range.commonAncestorContainer;
|
|
47
|
+
if (!container.contains(commonAncestor)) {
|
|
48
|
+
return false; // 整个范围不在容器内
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 严格模式检查:如果启用严格模式,检查选择范围内是否包含目标className的元素
|
|
52
|
+
if (this.options.strictMode) {
|
|
53
|
+
if (this.hasTargetClassInSelection(range)) {
|
|
54
|
+
return false; // 选择范围内包含目标className的元素,不允许高亮
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const highlightedElement = this.wrapRange(range);
|
|
60
|
+
|
|
61
|
+
this.highlights.push({
|
|
62
|
+
element: highlightedElement,
|
|
63
|
+
text: selection.toString(),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return true;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.warn("Could not highlight selection:", error);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Check if the selection range contains elements with the target class
|
|
75
|
+
* @param {Range} range - The range to check
|
|
76
|
+
*/
|
|
77
|
+
hasTargetClassInSelection(range) {
|
|
78
|
+
// Check if startContainer or endContainer themselves have the target class
|
|
79
|
+
const startElement =
|
|
80
|
+
range.startContainer.nodeType === Node.ELEMENT_NODE
|
|
81
|
+
? range.startContainer
|
|
82
|
+
: range.startContainer.parentElement;
|
|
83
|
+
|
|
84
|
+
const endElement =
|
|
85
|
+
range.endContainer.nodeType === Node.ELEMENT_NODE
|
|
86
|
+
? range.endContainer
|
|
87
|
+
: range.endContainer.parentElement;
|
|
88
|
+
|
|
89
|
+
if (
|
|
90
|
+
startElement &&
|
|
91
|
+
startElement.classList &&
|
|
92
|
+
startElement.classList.contains(this.options.className)
|
|
93
|
+
) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (
|
|
98
|
+
endElement &&
|
|
99
|
+
endElement.classList &&
|
|
100
|
+
endElement.classList.contains(this.options.className)
|
|
101
|
+
) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Create a temporary div to work with the range content
|
|
106
|
+
const tempDiv = document.createElement("div");
|
|
107
|
+
tempDiv.appendChild(range.cloneContents());
|
|
108
|
+
|
|
109
|
+
// Check if any child elements have the target class
|
|
110
|
+
const elementsWithTargetClass = tempDiv.querySelectorAll(
|
|
111
|
+
`.${this.options.className}`,
|
|
112
|
+
);
|
|
113
|
+
return elementsWithTargetClass.length > 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Wrap a range with highlight element
|
|
118
|
+
* @param {Range} range - The range to wrap
|
|
119
|
+
*/
|
|
120
|
+
wrapRange(range) {
|
|
121
|
+
const highlightEl = document.createElement("span");
|
|
122
|
+
highlightEl.className = this.options.className;
|
|
123
|
+
highlightEl.style.backgroundColor = this.options.color;
|
|
124
|
+
highlightEl.style.padding = "0";
|
|
125
|
+
highlightEl.style.margin = "0";
|
|
126
|
+
|
|
127
|
+
// Clone the range and insert the highlight element
|
|
128
|
+
range.surroundContents(highlightEl);
|
|
129
|
+
|
|
130
|
+
return highlightEl;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Remove all highlights
|
|
135
|
+
*/
|
|
136
|
+
removeAllHighlights() {
|
|
137
|
+
this.highlights.forEach((highlight) => {
|
|
138
|
+
if (highlight.element.parentNode) {
|
|
139
|
+
const textNode = document.createTextNode(highlight.text);
|
|
140
|
+
highlight.element.parentNode.replaceChild(
|
|
141
|
+
textNode,
|
|
142
|
+
highlight.element,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
this.highlights = [];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get all highlighted elements
|
|
152
|
+
*/
|
|
153
|
+
getHighlights() {
|
|
154
|
+
return [...this.highlights];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Remove a specific highlight
|
|
159
|
+
* @param {Object} highlight - The highlight object to remove
|
|
160
|
+
*/
|
|
161
|
+
removeHighlight(highlight) {
|
|
162
|
+
const index = this.highlights.indexOf(highlight);
|
|
163
|
+
if (index !== -1) {
|
|
164
|
+
// Restore the original text
|
|
165
|
+
if (highlight.element.parentNode) {
|
|
166
|
+
const textNode = document.createTextNode(highlight.text);
|
|
167
|
+
highlight.element.parentNode.replaceChild(
|
|
168
|
+
textNode,
|
|
169
|
+
highlight.element,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Remove from the highlights array
|
|
174
|
+
this.highlights.splice(index, 1);
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
module.exports = TextHighlighter;
|
|
182
|
+
//# sourceMappingURL=lk-text-select-highlight.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lk-text-select-highlight.cjs.js","sources":["../src/index.js"],"sourcesContent":["/**\r\n * lk-text-select-highlight - A lightweight text selection highlight library\r\n * @module lk-text-select-highlight\r\n */\r\n\r\nclass TextHighlighter {\r\n\tconstructor(options = {}) {\r\n\t\tthis.options = {\r\n\t\t\tclassName: options.className || \"highlighted-text\",\r\n\t\t\tcolor: options.color || \"#ffff00\",\r\n\t\t\tcontainer: options.container || document.body, // DOM容器,默认为整个body\r\n\t\t\tstrictMode: options.strictMode || false, // 严格模式,默认为false\r\n\t\t\tcaseSensitive:\r\n\t\t\t\toptions.caseSensitive !== undefined\r\n\t\t\t\t\t? options.caseSensitive\r\n\t\t\t\t\t: true,\r\n\t\t\t...options,\r\n\t\t};\r\n\r\n\t\tthis.highlights = [];\r\n\t}\r\n\r\n\t/**\r\n\t * Highlight selected text\r\n\t */\r\n\thighlightSelection() {\r\n\t\tconst selection = window.getSelection();\r\n\t\tif (!selection.toString().trim()) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tconst range = selection.getRangeAt(0);\r\n\r\n\t\t// 检查选择的范围是否在指定的容器内\r\n\t\tconst container = this.options.container;\r\n\t\tif (\r\n\t\t\t!container.contains(range.startContainer) ||\r\n\t\t\t!container.contains(range.endContainer)\r\n\t\t) {\r\n\t\t\treturn false; // 选择超出了指定容器范围\r\n\t\t}\r\n\r\n\t\t// 更全面地检查整个范围是否在容器内\r\n\t\tconst commonAncestor = range.commonAncestorContainer;\r\n\t\tif (!container.contains(commonAncestor)) {\r\n\t\t\treturn false; // 整个范围不在容器内\r\n\t\t}\r\n\r\n\t\t// 严格模式检查:如果启用严格模式,检查选择范围内是否包含目标className的元素\r\n\t\tif (this.options.strictMode) {\r\n\t\t\tif (this.hasTargetClassInSelection(range)) {\r\n\t\t\t\treturn false; // 选择范围内包含目标className的元素,不允许高亮\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tconst highlightedElement = this.wrapRange(range);\r\n\r\n\t\t\tthis.highlights.push({\r\n\t\t\t\telement: highlightedElement,\r\n\t\t\t\ttext: selection.toString(),\r\n\t\t\t});\r\n\r\n\t\t\treturn true;\r\n\t\t} catch (error) {\r\n\t\t\tconsole.warn(\"Could not highlight selection:\", error);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Check if the selection range contains elements with the target class\r\n\t * @param {Range} range - The range to check\r\n\t */\r\n\thasTargetClassInSelection(range) {\r\n\t\t// Check if startContainer or endContainer themselves have the target class\r\n\t\tconst startElement =\r\n\t\t\trange.startContainer.nodeType === Node.ELEMENT_NODE\r\n\t\t\t\t? range.startContainer\r\n\t\t\t\t: range.startContainer.parentElement;\r\n\r\n\t\tconst endElement =\r\n\t\t\trange.endContainer.nodeType === Node.ELEMENT_NODE\r\n\t\t\t\t? range.endContainer\r\n\t\t\t\t: range.endContainer.parentElement;\r\n\r\n\t\tif (\r\n\t\t\tstartElement &&\r\n\t\t\tstartElement.classList &&\r\n\t\t\tstartElement.classList.contains(this.options.className)\r\n\t\t) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\tif (\r\n\t\t\tendElement &&\r\n\t\t\tendElement.classList &&\r\n\t\t\tendElement.classList.contains(this.options.className)\r\n\t\t) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\t// Create a temporary div to work with the range content\r\n\t\tconst tempDiv = document.createElement(\"div\");\r\n\t\ttempDiv.appendChild(range.cloneContents());\r\n\r\n\t\t// Check if any child elements have the target class\r\n\t\tconst elementsWithTargetClass = tempDiv.querySelectorAll(\r\n\t\t\t`.${this.options.className}`,\r\n\t\t);\r\n\t\treturn elementsWithTargetClass.length > 0;\r\n\t}\r\n\r\n\t/**\r\n\t * Wrap a range with highlight element\r\n\t * @param {Range} range - The range to wrap\r\n\t */\r\n\twrapRange(range) {\r\n\t\tconst highlightEl = document.createElement(\"span\");\r\n\t\thighlightEl.className = this.options.className;\r\n\t\thighlightEl.style.backgroundColor = this.options.color;\r\n\t\thighlightEl.style.padding = \"0\";\r\n\t\thighlightEl.style.margin = \"0\";\r\n\r\n\t\t// Clone the range and insert the highlight element\r\n\t\trange.surroundContents(highlightEl);\r\n\r\n\t\treturn highlightEl;\r\n\t}\r\n\r\n\t/**\r\n\t * Remove all highlights\r\n\t */\r\n\tremoveAllHighlights() {\r\n\t\tthis.highlights.forEach((highlight) => {\r\n\t\t\tif (highlight.element.parentNode) {\r\n\t\t\t\tconst textNode = document.createTextNode(highlight.text);\r\n\t\t\t\thighlight.element.parentNode.replaceChild(\r\n\t\t\t\t\ttextNode,\r\n\t\t\t\t\thighlight.element,\r\n\t\t\t\t);\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t\tthis.highlights = [];\r\n\t}\r\n\r\n\t/**\r\n\t * Get all highlighted elements\r\n\t */\r\n\tgetHighlights() {\r\n\t\treturn [...this.highlights];\r\n\t}\r\n\r\n\t/**\r\n\t * Remove a specific highlight\r\n\t * @param {Object} highlight - The highlight object to remove\r\n\t */\r\n\tremoveHighlight(highlight) {\r\n\t\tconst index = this.highlights.indexOf(highlight);\r\n\t\tif (index !== -1) {\r\n\t\t\t// Restore the original text\r\n\t\t\tif (highlight.element.parentNode) {\r\n\t\t\t\tconst textNode = document.createTextNode(highlight.text);\r\n\t\t\t\thighlight.element.parentNode.replaceChild(\r\n\t\t\t\t\ttextNode,\r\n\t\t\t\t\thighlight.element,\r\n\t\t\t\t);\r\n\t\t\t}\r\n\r\n\t\t\t// Remove from the highlights array\r\n\t\t\tthis.highlights.splice(index, 1);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\nexport default TextHighlighter;\r\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lk-text-select-highlight - A lightweight text selection highlight library
|
|
3
|
+
* @module lk-text-select-highlight
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class TextHighlighter {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.options = {
|
|
9
|
+
className: options.className || "highlighted-text",
|
|
10
|
+
color: options.color || "#ffff00",
|
|
11
|
+
container: options.container || document.body, // DOM容器,默认为整个body
|
|
12
|
+
strictMode: options.strictMode || false, // 严格模式,默认为false
|
|
13
|
+
caseSensitive:
|
|
14
|
+
options.caseSensitive !== undefined
|
|
15
|
+
? options.caseSensitive
|
|
16
|
+
: true,
|
|
17
|
+
...options,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
this.highlights = [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Highlight selected text
|
|
25
|
+
*/
|
|
26
|
+
highlightSelection() {
|
|
27
|
+
const selection = window.getSelection();
|
|
28
|
+
if (!selection.toString().trim()) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const range = selection.getRangeAt(0);
|
|
33
|
+
|
|
34
|
+
// 检查选择的范围是否在指定的容器内
|
|
35
|
+
const container = this.options.container;
|
|
36
|
+
if (
|
|
37
|
+
!container.contains(range.startContainer) ||
|
|
38
|
+
!container.contains(range.endContainer)
|
|
39
|
+
) {
|
|
40
|
+
return false; // 选择超出了指定容器范围
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 更全面地检查整个范围是否在容器内
|
|
44
|
+
const commonAncestor = range.commonAncestorContainer;
|
|
45
|
+
if (!container.contains(commonAncestor)) {
|
|
46
|
+
return false; // 整个范围不在容器内
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 严格模式检查:如果启用严格模式,检查选择范围内是否包含目标className的元素
|
|
50
|
+
if (this.options.strictMode) {
|
|
51
|
+
if (this.hasTargetClassInSelection(range)) {
|
|
52
|
+
return false; // 选择范围内包含目标className的元素,不允许高亮
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const highlightedElement = this.wrapRange(range);
|
|
58
|
+
|
|
59
|
+
this.highlights.push({
|
|
60
|
+
element: highlightedElement,
|
|
61
|
+
text: selection.toString(),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return true;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.warn("Could not highlight selection:", error);
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if the selection range contains elements with the target class
|
|
73
|
+
* @param {Range} range - The range to check
|
|
74
|
+
*/
|
|
75
|
+
hasTargetClassInSelection(range) {
|
|
76
|
+
// Check if startContainer or endContainer themselves have the target class
|
|
77
|
+
const startElement =
|
|
78
|
+
range.startContainer.nodeType === Node.ELEMENT_NODE
|
|
79
|
+
? range.startContainer
|
|
80
|
+
: range.startContainer.parentElement;
|
|
81
|
+
|
|
82
|
+
const endElement =
|
|
83
|
+
range.endContainer.nodeType === Node.ELEMENT_NODE
|
|
84
|
+
? range.endContainer
|
|
85
|
+
: range.endContainer.parentElement;
|
|
86
|
+
|
|
87
|
+
if (
|
|
88
|
+
startElement &&
|
|
89
|
+
startElement.classList &&
|
|
90
|
+
startElement.classList.contains(this.options.className)
|
|
91
|
+
) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (
|
|
96
|
+
endElement &&
|
|
97
|
+
endElement.classList &&
|
|
98
|
+
endElement.classList.contains(this.options.className)
|
|
99
|
+
) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Create a temporary div to work with the range content
|
|
104
|
+
const tempDiv = document.createElement("div");
|
|
105
|
+
tempDiv.appendChild(range.cloneContents());
|
|
106
|
+
|
|
107
|
+
// Check if any child elements have the target class
|
|
108
|
+
const elementsWithTargetClass = tempDiv.querySelectorAll(
|
|
109
|
+
`.${this.options.className}`,
|
|
110
|
+
);
|
|
111
|
+
return elementsWithTargetClass.length > 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Wrap a range with highlight element
|
|
116
|
+
* @param {Range} range - The range to wrap
|
|
117
|
+
*/
|
|
118
|
+
wrapRange(range) {
|
|
119
|
+
const highlightEl = document.createElement("span");
|
|
120
|
+
highlightEl.className = this.options.className;
|
|
121
|
+
highlightEl.style.backgroundColor = this.options.color;
|
|
122
|
+
highlightEl.style.padding = "0";
|
|
123
|
+
highlightEl.style.margin = "0";
|
|
124
|
+
|
|
125
|
+
// Clone the range and insert the highlight element
|
|
126
|
+
range.surroundContents(highlightEl);
|
|
127
|
+
|
|
128
|
+
return highlightEl;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Remove all highlights
|
|
133
|
+
*/
|
|
134
|
+
removeAllHighlights() {
|
|
135
|
+
this.highlights.forEach((highlight) => {
|
|
136
|
+
if (highlight.element.parentNode) {
|
|
137
|
+
const textNode = document.createTextNode(highlight.text);
|
|
138
|
+
highlight.element.parentNode.replaceChild(
|
|
139
|
+
textNode,
|
|
140
|
+
highlight.element,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
this.highlights = [];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get all highlighted elements
|
|
150
|
+
*/
|
|
151
|
+
getHighlights() {
|
|
152
|
+
return [...this.highlights];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Remove a specific highlight
|
|
157
|
+
* @param {Object} highlight - The highlight object to remove
|
|
158
|
+
*/
|
|
159
|
+
removeHighlight(highlight) {
|
|
160
|
+
const index = this.highlights.indexOf(highlight);
|
|
161
|
+
if (index !== -1) {
|
|
162
|
+
// Restore the original text
|
|
163
|
+
if (highlight.element.parentNode) {
|
|
164
|
+
const textNode = document.createTextNode(highlight.text);
|
|
165
|
+
highlight.element.parentNode.replaceChild(
|
|
166
|
+
textNode,
|
|
167
|
+
highlight.element,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Remove from the highlights array
|
|
172
|
+
this.highlights.splice(index, 1);
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export { TextHighlighter as default };
|
|
180
|
+
//# sourceMappingURL=lk-text-select-highlight.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lk-text-select-highlight.esm.js","sources":["../src/index.js"],"sourcesContent":["/**\r\n * lk-text-select-highlight - A lightweight text selection highlight library\r\n * @module lk-text-select-highlight\r\n */\r\n\r\nclass TextHighlighter {\r\n\tconstructor(options = {}) {\r\n\t\tthis.options = {\r\n\t\t\tclassName: options.className || \"highlighted-text\",\r\n\t\t\tcolor: options.color || \"#ffff00\",\r\n\t\t\tcontainer: options.container || document.body, // DOM容器,默认为整个body\r\n\t\t\tstrictMode: options.strictMode || false, // 严格模式,默认为false\r\n\t\t\tcaseSensitive:\r\n\t\t\t\toptions.caseSensitive !== undefined\r\n\t\t\t\t\t? options.caseSensitive\r\n\t\t\t\t\t: true,\r\n\t\t\t...options,\r\n\t\t};\r\n\r\n\t\tthis.highlights = [];\r\n\t}\r\n\r\n\t/**\r\n\t * Highlight selected text\r\n\t */\r\n\thighlightSelection() {\r\n\t\tconst selection = window.getSelection();\r\n\t\tif (!selection.toString().trim()) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tconst range = selection.getRangeAt(0);\r\n\r\n\t\t// 检查选择的范围是否在指定的容器内\r\n\t\tconst container = this.options.container;\r\n\t\tif (\r\n\t\t\t!container.contains(range.startContainer) ||\r\n\t\t\t!container.contains(range.endContainer)\r\n\t\t) {\r\n\t\t\treturn false; // 选择超出了指定容器范围\r\n\t\t}\r\n\r\n\t\t// 更全面地检查整个范围是否在容器内\r\n\t\tconst commonAncestor = range.commonAncestorContainer;\r\n\t\tif (!container.contains(commonAncestor)) {\r\n\t\t\treturn false; // 整个范围不在容器内\r\n\t\t}\r\n\r\n\t\t// 严格模式检查:如果启用严格模式,检查选择范围内是否包含目标className的元素\r\n\t\tif (this.options.strictMode) {\r\n\t\t\tif (this.hasTargetClassInSelection(range)) {\r\n\t\t\t\treturn false; // 选择范围内包含目标className的元素,不允许高亮\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tconst highlightedElement = this.wrapRange(range);\r\n\r\n\t\t\tthis.highlights.push({\r\n\t\t\t\telement: highlightedElement,\r\n\t\t\t\ttext: selection.toString(),\r\n\t\t\t});\r\n\r\n\t\t\treturn true;\r\n\t\t} catch (error) {\r\n\t\t\tconsole.warn(\"Could not highlight selection:\", error);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Check if the selection range contains elements with the target class\r\n\t * @param {Range} range - The range to check\r\n\t */\r\n\thasTargetClassInSelection(range) {\r\n\t\t// Check if startContainer or endContainer themselves have the target class\r\n\t\tconst startElement =\r\n\t\t\trange.startContainer.nodeType === Node.ELEMENT_NODE\r\n\t\t\t\t? range.startContainer\r\n\t\t\t\t: range.startContainer.parentElement;\r\n\r\n\t\tconst endElement =\r\n\t\t\trange.endContainer.nodeType === Node.ELEMENT_NODE\r\n\t\t\t\t? range.endContainer\r\n\t\t\t\t: range.endContainer.parentElement;\r\n\r\n\t\tif (\r\n\t\t\tstartElement &&\r\n\t\t\tstartElement.classList &&\r\n\t\t\tstartElement.classList.contains(this.options.className)\r\n\t\t) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\tif (\r\n\t\t\tendElement &&\r\n\t\t\tendElement.classList &&\r\n\t\t\tendElement.classList.contains(this.options.className)\r\n\t\t) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\t// Create a temporary div to work with the range content\r\n\t\tconst tempDiv = document.createElement(\"div\");\r\n\t\ttempDiv.appendChild(range.cloneContents());\r\n\r\n\t\t// Check if any child elements have the target class\r\n\t\tconst elementsWithTargetClass = tempDiv.querySelectorAll(\r\n\t\t\t`.${this.options.className}`,\r\n\t\t);\r\n\t\treturn elementsWithTargetClass.length > 0;\r\n\t}\r\n\r\n\t/**\r\n\t * Wrap a range with highlight element\r\n\t * @param {Range} range - The range to wrap\r\n\t */\r\n\twrapRange(range) {\r\n\t\tconst highlightEl = document.createElement(\"span\");\r\n\t\thighlightEl.className = this.options.className;\r\n\t\thighlightEl.style.backgroundColor = this.options.color;\r\n\t\thighlightEl.style.padding = \"0\";\r\n\t\thighlightEl.style.margin = \"0\";\r\n\r\n\t\t// Clone the range and insert the highlight element\r\n\t\trange.surroundContents(highlightEl);\r\n\r\n\t\treturn highlightEl;\r\n\t}\r\n\r\n\t/**\r\n\t * Remove all highlights\r\n\t */\r\n\tremoveAllHighlights() {\r\n\t\tthis.highlights.forEach((highlight) => {\r\n\t\t\tif (highlight.element.parentNode) {\r\n\t\t\t\tconst textNode = document.createTextNode(highlight.text);\r\n\t\t\t\thighlight.element.parentNode.replaceChild(\r\n\t\t\t\t\ttextNode,\r\n\t\t\t\t\thighlight.element,\r\n\t\t\t\t);\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t\tthis.highlights = [];\r\n\t}\r\n\r\n\t/**\r\n\t * Get all highlighted elements\r\n\t */\r\n\tgetHighlights() {\r\n\t\treturn [...this.highlights];\r\n\t}\r\n\r\n\t/**\r\n\t * Remove a specific highlight\r\n\t * @param {Object} highlight - The highlight object to remove\r\n\t */\r\n\tremoveHighlight(highlight) {\r\n\t\tconst index = this.highlights.indexOf(highlight);\r\n\t\tif (index !== -1) {\r\n\t\t\t// Restore the original text\r\n\t\t\tif (highlight.element.parentNode) {\r\n\t\t\t\tconst textNode = document.createTextNode(highlight.text);\r\n\t\t\t\thighlight.element.parentNode.replaceChild(\r\n\t\t\t\t\ttextNode,\r\n\t\t\t\t\thighlight.element,\r\n\t\t\t\t);\r\n\t\t\t}\r\n\r\n\t\t\t// Remove from the highlights array\r\n\t\t\tthis.highlights.splice(index, 1);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n}\r\n\r\nexport default TextHighlighter;\r\n"],"names":[],"mappings":"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"}
|