A lightweight, zero‑dependency JavaScript library to spotlight DOM elements with a customizable overlay, border, padding, and optional tooltip. Single or multiple elements can be highlighted, using usual selectors.
target-highlight can be used for example to setup a tutorial by highlighting elements.
- Overlay the page, carving out “holes” around target elements
- Customizable overlay color, border color, width, radius, padding
- Tooltip support (single or per‑element) with automatic positioning
- Configurable via options or global defaults
npm install target-highlight
# or
yarn add target-highlightimport {
  targetHighlight,
  targetHide,
  defaultConfig,
  HighlightOptions,
} from "target-highlight";
// Define options
const options = {
  overlayColor: "#00000080", // all color formats will work
  borderColor: "red", // all color formats will work
  singleTooltip: true,
  padding: "2px", // same as css
  borderRadius: 2,
  overlayZIndex: 2,
  hidePointerEvents: false,
  scrollToTarget: {
    // Use the same options as the native scrollIntoView api
    behavior: "smooth",
    block: "center",
    inline: "center",
  },
  nextCallback: () => {}, // use your callback when clicking on a button with an id of target-highlight-button-previous
  previousCallback: () => {}, // use your callback when clicking on a button with an id of target-highlight-button-previous
  stopCallback: () => {}, // use your callback when clicking on a button with an id of target-highlight-button-stop
  tooltip: "My content", // can also be a callback returning html content
  forceTooltipPosition: null, // 'top' | 'right' | 'bottom' | 'left' | null, default: null
  useResizeObserver: true, // If true, will trigger a re-render when the highlighted element resizes
  blockedKeys: [], // Add keyboard keys that will be inactive when the application is running
};
// Target an element using any selector
targetHighlight("#myDiv", {
  ...options,
  tooltip: "This is my div",
});
// Or target many elements
targetHighlight(".myClass", options);
// Remove all highlights
targetHide();Target the following css class to customize the tooltip:
.target-highlight-tooltip {
}The tooltip can also be passed a callback returning html content:
targetHighlight("#myDiv", {
  ...config,
  tooltip: () => {
    return `
      <div>
        My custom tooltip
      </div>
    `;
  },
});When highlighting multiple elements, you may need to show individual tooltips on each element. Use the data-target-highlight-tooltip attribute on the highlighted elements to setup the individual tooltip contents. You can also use the data-target-highlight-tooltip-position to force the tooltip position ('top' | 'right' | 'bottom' | 'left'). In this case, you won't need the tooltip option normally passed to targetHighlight. Bear in mind using data attributes, the tooltip content can only be a string.
<div
  class="mySelection"
  data-target-highlight-tooltip="This is div 1"
  data-target-highlight-tooltip-position="bottom"
>
  Some content
</div>
<div
  class="mySelection"
  data-target-highlight-tooltip="This is div 2"
  data-target-highlight-tooltip-position="left"
>
  Some other content
</div>You can create a scenario to move from step to step. Add data-step="step" on the elements part of the scenario, where step is a number.
// Define step variables
const step = {
  current: 0,
  max: 5,
};
// Define callbacks in options
const options = {
  nextCallback: () => moveStep("next"),
  previousCallback: () => moveStep("previous"),
  stopCallback: targetHide,
};
// Define blocked keys
const blockedKeys = [" ", "Tab"];
// Define a function to call the library and apply event listeners
// In this example, chevron icons are added as plain svg
// Buttons with id #target-highlight-button-previous and #target-highlight-button-next will be recognized by the library, and events attached to them.
function applySteps() {
  targetHighlight(`[data-step="${step.current}"]`, {
    ...options,
    blockedKeys,
    tooltip: () => {
      return `<div style="position:relative; padding: 0 24px">This is step ${step.value}</div><button id="target-highlight-button-previous" style="position: absolute; top: 50%; left: 0; transform: translateY(-50%)">${chevronLeftIcon}</button><button id="target-highlight-button-next" style="position: absolute; top: 50%; right: 0; transform: translateY(-50%)">${chevronRightIcon}</button>`;
    },
  });
  setTimeout(() => {
    applyStepListeners(options);
  }, 10);
}
// Define a function to move through the steps
function moveStep(direction) {
  if (direction === "next") {
    step.current += 1;
    if (step.current > step.max) {
      step.current = 0;
    }
  } else {
    step.current -= 1;
    if (step.current < 0) {
      step.current = step.max;
    }
  }
  applySteps();
}Use the data-target-highlight ignore data attribute on elements never to be highlighted:
<!-- When targeting the .selection class, only one element will be highlighted -->
<div class="selection">I will be selected</div>
<div class="selection" data-target-highlight-ignore>I will not be selected</div>Combine hidePointerEvents: false with blocked keys, to prevent events outside of the tour.
List of all the available blockable keys:
type KeyboardNavigationKey =
  | " "
  | "Tab"
  | "Enter"
  | "ArrowUp"
  | "ArrowDown"
  | "ArrowLeft"
  | "ArrowRight"
  | "Home"
  | "End"
  | "PageUp"
  | "PageDown"
  | "Escape";Set blocked keys in the options object:
blockedKeys: [
  " ",
  "Tab",
  "Enter",
  "ArrowUp",
  "ArrowDown",
  "ArrowLeft",
  "ArrowRight",
  "Home",
  "End",
  "PageUp",
  "PageDown",
  "Escape",
];Blocked events will be restored when targetHide is called.
This library does not inject CSS. To enable tooltip transitions, add for example the following to your app’s global styles:
.target-highlight-tooltip {
  opacity: 1;
  transition: opacity 0.3s;
  will-change: opacity;
}
.target-highlight-tooltip.fade-in {
  opacity: 0;
  animation: fadeIn 0.3s forwards;
}
.target-highlight-tooltip.fade-out {
  opacity: 1;
  animation: fadeOut 0.3s forwards;
  pointer-events: none;
}
@keyframes fadeIn {
  to {
    opacity: 1;
  }
}
@keyframes fadeOut {
  to {
    opacity: 0;
  }
}