HoverMenuExtender

March 15, 2026 · View on GitHub

The HoverMenuExtender displays a popup panel when the user hovers over a target control. It supports configurable show/hide delays, positional placement, and hover CSS styling. The popup remains visible while the mouse is over either the target or the popup itself.

Original Ajax Control Toolkit documentation: https://www.asp.net/ajax/ajaxcontroltoolkit/HoverMenuExtender

Features Supported in Blazor

  • TargetControlID — ID of the control that triggers the hover menu
  • PopupControlID — ID of the panel element to display as a hover menu
  • PopupPosition — Position of the popup relative to the target
  • OffsetX — Horizontal offset in pixels from the calculated position
  • OffsetY — Vertical offset in pixels from the calculated position
  • PopDelay — Delay in milliseconds before showing the popup
  • HoverDelay — Delay in milliseconds before hiding the popup
  • HoverCssClass — CSS class applied to the target while the hover menu is visible
  • Enabled — Enable or disable the extender behavior
  • BehaviorID — Optional identifier for JavaScript behavior lookup

PopupPosition Enum

enum PopupPosition
{
    Left = 0,     // To the left of target
    Right = 1,    // To the right of target (default for HoverMenuExtender)
    Top = 2,      // Above target
    Bottom = 3,   // Below target
    Center = 4    // Centered on target
}

Web Forms Syntax

<asp:Panel ID="pnlRow" runat="server" CssClass="data-row">
    <span>Row content here</span>
</asp:Panel>

<asp:Panel ID="pnlActions" runat="server" style="display: none;" CssClass="hover-actions">
    <asp:Button ID="btnEdit" Text="Edit" runat="server" />
    <asp:Button ID="btnDelete" Text="Delete" runat="server" />
</asp:Panel>

<ajaxToolkit:HoverMenuExtender
    ID="hme1"
    runat="server"
    TargetControlID="pnlRow"
    PopupControlID="pnlActions"
    PopupPosition="Right"
    HoverDelay="200"
    PopDelay="100"
    HoverCssClass="row-hover"
    OffsetX="5" />

Blazor Migration

<div ID="pnlRow" class="data-row">
    <span>Row content here</span>
</div>

<div ID="pnlActions" style="display: none;" class="hover-actions">
    <Button ID="btnEdit" Text="Edit" />
    <Button ID="btnDelete" Text="Delete" />
</div>

<HoverMenuExtender
    TargetControlID="pnlRow"
    PopupControlID="pnlActions"
    PopupPosition="PopupPosition.Right"
    HoverDelay="200"
    PopDelay="100"
    HoverCssClass="row-hover"
    OffsetX="5" />

Migration is simple: Remove the ajaxToolkit: prefix and runat="server". Use full enum type name for PopupPosition. Everything else stays the same!

Properties Reference

PropertyTypeDefaultDescription
TargetControlIDstring(required)ID of the control that triggers the hover menu
PopupControlIDstring""ID of the panel element to display as a hover menu
PopupPositionPopupPositionRightPosition of the popup relative to the target: Left, Right, Top, Bottom, Center
OffsetXint0Horizontal offset in pixels from the calculated position
OffsetYint0Vertical offset in pixels from the calculated position
PopDelayint0Delay in milliseconds before showing the popup after mouse enters the target
HoverDelayint300Delay in milliseconds before hiding the popup after mouse leaves the target and popup
HoverCssClassstring""CSS class applied to the target control while the hover menu is visible
BehaviorIDstringTargetControlIDOptional identifier for JavaScript behavior lookup
EnabledbooltrueWhether the extender is active

Usage Examples

Basic Hover Menu

@rendermode InteractiveServer

<div ID="targetItem" style="padding: 10px; border: 1px solid #ddd; cursor: pointer;">
    Hover over me to see actions
</div>

<div ID="hoverPanel" style="display: none; background: white; border: 1px solid #ccc; padding: 10px; box-shadow: 0 2px 8px rgba(0,0,0,0.15);">
    <button>Edit</button>
    <button>Delete</button>
    <button>Share</button>
</div>

<HoverMenuExtender
    TargetControlID="targetItem"
    PopupControlID="hoverPanel"
    PopupPosition="PopupPosition.Right"
    HoverDelay="200" />

Row Actions on Hover

@rendermode InteractiveServer

@foreach (var item in items)
{
    <div ID="@($"row-{item.Id}")" class="list-row">
        <span>@item.Name</span>
    </div>

    <div ID="@($"actions-{item.Id}")" style="display: none;" class="action-panel">
        <button onclick="@(() => Edit(item.Id))">✏️ Edit</button>
        <button onclick="@(() => Delete(item.Id))">🗑️ Delete</button>
    </div>

    <HoverMenuExtender
        TargetControlID="@($"row-{item.Id}")"
        PopupControlID="@($"actions-{item.Id}")"
        PopupPosition="PopupPosition.Right"
        HoverDelay="300"
        HoverCssClass="row-highlighted"
        OffsetX="10" />
}

<style>
    .list-row { padding: 10px; border-bottom: 1px solid #eee; }
    .row-highlighted { background: #f0f8ff; }
    .action-panel { background: white; border: 1px solid #ddd; padding: 8px; border-radius: 4px; }
</style>

@code {
    private List<Item> items = new();
    void Edit(int id) { /* Edit logic */ }
    void Delete(int id) { /* Delete logic */ }
}

Tooltip-Style Hover Menu

@rendermode InteractiveServer

<span ID="helpIcon" style="cursor: help; font-size: 1.2em;">ℹ️</span>

<div ID="helpTooltip" style="display: none; background: #333; color: white; padding: 12px; border-radius: 6px; max-width: 250px; font-size: 0.9em;">
    <p>This field accepts values between 1 and 100. Leave blank for the default value.</p>
</div>

<HoverMenuExtender
    TargetControlID="helpIcon"
    PopupControlID="helpTooltip"
    PopupPosition="PopupPosition.Bottom"
    PopDelay="200"
    HoverDelay="500"
    OffsetY="5" />

Hover Menu with Show Delay

@rendermode InteractiveServer

<div ID="menuTrigger" style="padding: 8px 16px; background: #007bff; color: white; display: inline-block; border-radius: 4px;">
    More Options ▼
</div>

<div ID="menuPopup" style="display: none; background: white; border: 1px solid #ddd; min-width: 150px; box-shadow: 0 4px 12px rgba(0,0,0,0.1);">
    <div style="padding: 8px 16px; cursor: pointer;">Option 1</div>
    <div style="padding: 8px 16px; cursor: pointer;">Option 2</div>
    <div style="padding: 8px 16px; cursor: pointer;">Option 3</div>
</div>

<HoverMenuExtender
    TargetControlID="menuTrigger"
    PopupControlID="menuPopup"
    PopupPosition="PopupPosition.Bottom"
    PopDelay="100"
    HoverDelay="400" />

HTML Output

The HoverMenuExtender produces no HTML itself — it attaches JavaScript behavior to show/hide the popup panel on mouse enter/leave events.

JavaScript Interop

The HoverMenuExtender loads hover-menu-extender.js as an ES module. JavaScript handles:

  • Monitoring mouse enter/leave events on the target and popup elements
  • Applying show/hide delays (PopDelay and HoverDelay)
  • Positioning the popup relative to the target with offsets
  • Adding/removing the HoverCssClass on the target element
  • Keeping the popup visible when the mouse moves between target and popup

Render Mode Requirements

The HoverMenuExtender requires InteractiveServer render mode:

@rendermode InteractiveServer

Graceful Degradation

  • SSR/Static mode: The extender silently skips initialization. The popup panel remains hidden.
  • JavaScript disabled: Same as SSR — target control functions normally without hover behavior.

Migration Notes

From Web Forms Ajax Toolkit

  1. Remove ajaxToolkit: prefix

    - <ajaxToolkit:HoverMenuExtender
    + <HoverMenuExtender
    
  2. Remove runat="server" and ID attributes

  3. Use full enum type name for PopupPosition

    - PopupPosition="Right"
    + PopupPosition="PopupPosition.Right"
    

Before (Web Forms)

<asp:Panel ID="pnlTarget" runat="server">
    <span>Hover me</span>
</asp:Panel>

<asp:Panel ID="pnlMenu" runat="server" style="display: none;">
    <a href="#">Edit</a> | <a href="#">Delete</a>
</asp:Panel>

<ajaxToolkit:HoverMenuExtender
    ID="hme1"
    TargetControlID="pnlTarget"
    PopupControlID="pnlMenu"
    PopupPosition="Right"
    HoverDelay="200"
    runat="server" />

After (Blazor)

<div ID="pnlTarget">
    <span>Hover me</span>
</div>

<div ID="pnlMenu" style="display: none;">
    <a href="#">Edit</a> | <a href="#">Delete</a>
</div>

<HoverMenuExtender
    TargetControlID="pnlTarget"
    PopupControlID="pnlMenu"
    PopupPosition="PopupPosition.Right"
    HoverDelay="200" />

Best Practices

  1. Set appropriate delaysPopDelay of 100–200ms prevents accidental triggers; HoverDelay of 200–500ms gives users time to reach the popup
  2. Hide the popup initially — Set style="display: none;" on the popup element
  3. Use HoverCssClass — Highlight the target to show the hover menu is active
  4. Position wisely — Use Right or Bottom for most use cases; adjust with offsets
  5. Keep popup content interactive — Users should be able to interact with buttons/links in the popup

Troubleshooting

IssueSolution
Popup not appearingVerify TargetControlID and PopupControlID match their elements' IDs. Ensure @rendermode InteractiveServer is set.
Popup disappears too quicklyIncrease HoverDelay to give users more time (300–500ms).
Popup appears too slowlyDecrease PopDelay (0–100ms).
Popup positioned incorrectlyTry a different PopupPosition value or adjust OffsetX/OffsetY.
HoverCssClass not appliedVerify the CSS class name is correct and the styles are defined.

See Also