---
title: Bulk Labeling for Text Spans with Keyboard Shortcut
short: Bulk Labeling for Text Spans
type: plugins
category: Automation
cat: automation
order: 10
meta_title: Bulk Labeling for Text Spans
meta_description: Assigns labels to all occurrences of the selected text at once
tier: enterprise
---
!!! note
For information about modifying this plugin or creating your own custom plugins, see [Customize and Build Your Own Plugins](custom).
For general plugin information, see [Plugins for projects](/guide/plugins) and [Plugin FAQ](faq).
## About
This plugin automatically applies the same label to all matching text spans when you press the **Shift** key.
For example, if you apply the `PER` label to the text span `Smith`, this plugin will automatically find all instances of `Smith` in the text and apply the `PER` label to them.

1. **Shift key tracking**
- The plugin tracks the state of the Shift key using `keydown` and `keyup` event listeners. A boolean variable `isShiftKeyPressed` is set to `true` when the Shift key is pressed and `false` when it is released.
2. **Bulk deletion**
- When a region (annotation) is deleted and the Shift key is pressed, the plugin identifies all regions with the same text and label as the deleted region.
- It then deletes all these matching regions to facilitate bulk deletion.
3. **Bulk creation**
- When a region is created and the Shift key is pressed, the plugin searches for all occurrences of the created region's text within the document.
- It creates new regions for each occurrence of the text, ensuring that no duplicate regions are created (i.e., regions with overlapping start and end offsets are avoided).
- The plugin also prevents tagging of single characters to avoid unnecessary annotations.
4. **Timeout**
- To prevent rapid consecutive bulk operations, the plugin sets a timeout of 1 second. This ensures that bulk operations are not triggered too frequently.
## Plugin
```javascript
/**
* Automatically creates text regions for all instances of the selected text and deletes existing regions
* when the shift key is pressed.
*/
// Track the state of the shift key
let isShiftKeyPressed = false;
window.addEventListener("keydown", (e) => {
if (e.key === "Shift") {
isShiftKeyPressed = true;
}
});
window.addEventListener("keyup", (e) => {
if (e.key === "Shift") {
isShiftKeyPressed = false;
}
});
LSI.on("entityDelete", (region) => {
if (!isShiftKeyPressed) return; // Only proceed if the shift key is pressed
if (window.BULK_REGIONS) return;
window.BULK_REGIONS = true;
setTimeout(() => {
window.BULK_REGIONS = false;
}, 1000);
const existingEntities = Htx.annotationStore.selected.regions;
const regionsToDelete = existingEntities.filter((entity) => {
const deletedText = region.text.toLowerCase().replace("\\\\n", " ");
const otherText = entity.text.toLowerCase().replace("\\\\n", " ");
return deletedText === otherText && region.labels[0] === entity.labels[0];
});
for (const region of regionsToDelete) {
Htx.annotationStore.selected.deleteRegion(region);
}
Htx.annotationStore.selected.updateObjects();
});
LSI.on("entityCreate", (region) => {
if (!isShiftKeyPressed) return;
if (window.BULK_REGIONS) return;
window.BULK_REGIONS = true;
setTimeout(() => {
window.BULK_REGIONS = false;
}, 1000);
const existingEntities = Htx.annotationStore.selected.regions;
setTimeout(() => {
// Prevent tagging a single character
if (region.text.length < 2) return;
regexp = new RegExp(
region.text.replace("\\\\n", "\\\\s+").replace(" ", "\\\\s+"),
"gi",
);
const matches = Array.from(region.object._value.matchAll(regexp));
for (const match of matches) {
if (match.index === region.startOffset) continue;
const startOffset = match.index;
const endOffset = match.index + region.text.length;
// Check for existing entities with overlapping start and end offset
let isDuplicate = false;
for (const entity of existingEntities) {
if (
startOffset <= entity.globalOffsets.end &&
entity.globalOffsets.start <= endOffset
) {
isDuplicate = true;
break;
}
}
if (!isDuplicate) {
Htx.annotationStore.selected.createResult(
{
text: region.text,
start: "/span[1]/text()[1]",
startOffset: startOffset,
end: "/span[1]/text()[1]",
endOffset: endOffset,
},
{
labels: [...region.labeling.value.labels],
},
region.labeling.from_name,
region.object,
);
}
}
Htx.annotationStore.selected.updateObjects();
}, 100);
});
```
**Related LSI instance methods:**
* [on(eventName, handler)](custom#LSI-on-eventName-handler)
**Related frontend APIs:**
* [regions](custom#regions)
**Related frontend events:**
* [entityCreate](/guide/frontend_reference#entityCreate)
* [entityDelete](/guide/frontend_reference#entityDelete)
## Labeling config
This is a basic NER labeling config.
```xml
```
**Related tags:**
* [View](/tags/view.html)
* [Text](/tags/text.html)
* [Labels](/tags/labels.html)
## Sample data
```json
[
{
"data": {
"text": "Opossums are nocturnal animals that are often seen scavenging for food at night. They have a prehensile tail that helps them climb trees and navigate their environment."
}
},
{
"data": {
"text": "Opossums are known for their ability to play dead when threatened, a behavior known as 'playing possum'. This act can deter predators and give the opossum a chance to escape."
}
},
{
"data": {
"text": "Opossums are marsupials, meaning they carry and nurse their young in a pouch. Baby opossums, called joeys, stay in the pouch for about two months after birth."
}
}
]
```