Determine the Placement of Bootstrap Popover by Utilizing SVG and Mouse Coordinates

Is there a way to dynamically reposition the popover that currently appears to the right of the Circle SVG? I would like to either base its position on the user's mouse click within the CircleSVG or move it to the exact center of the SVG, including both the popover content and arrow.

const popover = new bootstrap.Popover(document.getElementById("test"), {
                        html: true,
                        sanitize: false,
                        trigger: 'manual',
                        content: 'Nice'
                    });
                    
function togglePopover() {
  popover.toggle();
}
<link href="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="57353838232423253627176279677965">[email protected]</a>/dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="07656868737473756677473229372935">[email protected]</a>/dist/js/bootstrap.bundle.min.js"></script>

<svg onclick="togglePopover()"  height="100" width="100">
  <circle id="test" cx="50" cy="50" r="40" stroke="black" stroke width="3" fill="red" />
</svg>

Answer №1

Of course, it is entirely feasible to modify the location of the popover based on the mouse's position. This can be achieved by incorporating a <style> element into the HTML document dynamically whenever the SVG is clicked and the popover is displayed.

const tooltip = new bootstrap.Tooltip(document.getElementById("example"), {
  html: true,
  sanitize: false,
  trigger: 'manual',
  content: 'Great'
});

function applyStyles(css) {
  const currentStyleElement = document.querySelector('#customTooltipStyle');

  if (currentStyleElement) {
    currentStyleElement.remove();
  }

  document.head.insertAdjacentHTML('beforeend', `
    <style id="customTooltipStyle">${css}</style>
  `);
}

document.querySelector('#mySVG').addEventListener('click', function toggleTooltip(event) {
  tooltip.toggle();

  if (tooltip._hoverState) {
    const tooltipArrowWidth = tooltip.tip.querySelector('.tooltip-arrow').getBoundingClientRect().width;

    const tooltipPlacementSettings = {
      left: {
        x: `calc(${event.pageX - tooltipArrowWidth}px - 100%)`,
        y: `calc(${event.pageY}px - 50%)`
      },
      right: {
        x: `${event.pageX + tooltipArrowWidth}px`,
        y: `calc(${event.pageY}px - 50%)`
      },
      top: {
        x: `calc(${event.pageX}px - 50%)`,
        y: `calc(${event.pageY - tooltipArrowWidth / 2}px - 100%)`
      },
      bottom: {
        x: `calc(${event.pageX}px - 50%)`,
        y: `${event.pageY + tooltipArrowWidth / 2}px`
      }
    }

    setTimeout(() => {
      applyStyles(`
      .my-tooltip {
        inset: 0 auto auto 0 !important;
        transform: translate(${tooltipPlacementSettings[tooltip._popper.state.placement].x}, ${tooltipPlacementSettings[tooltip._popper.state.placement].y}) !important;
      }
    `);
      tooltip.tip.classList.add('my-tooltip');
    }, 0)
  }
})
<link href="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="60020f0f14131412011020554e504e52">[email protected]</a>/dist/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1c7e7373686f686e7d6c5c29322c322e">[email protected]</a>/dist/js/bootstrap.bundle.min.js"></script>

<svg id="mySVG" height="100" width="100">
  <circle id="example" cx="50" cy="50" r="40" stroke="black" stroke width="3" fill="red" />
</svg>

Answer №2

Today, you have brought up a couple of queries, both of which I believe can be addressed by dynamically creating the popover during the click event.

To position the popover in the middle of the element, one can calculate and provide an "offset" option. Refer to the red circle & the togglePopover_toCenter function in the example

For pointing the popover to where the mouse is located at the time of clicking, it requires attaching an event listener that utilizes the mouse coordinates to adjust the popover's positioning upon generation. Check out the blue circle & the togglePopover_toMouse function in the sample provided

function togglePopover_toCenter(event) {
  const targetElement = event.target;
  
  // Dispose of any existing popover for this element.
  const oldPopover = bootstrap.Popover.getInstance(targetElement);
  if (oldPopover){
    oldPopover.dispose();
  }
  
  const generatePopover = (element) => {
    // Obtain the element's width
    const boundingRect = element.getBoundingClientRect();
    const elementWidth = boundingRect.width;

    // Calculate the necessary "distance" value for the offset option
    // Half of the element's width, converted to negative, adding back the default 8 for the triangle
    const offsetDistance = (elementWidth/2 * -1) + 8;
  
    // Set up the popover using the offset attribute
    const popover = new bootstrap.Popover(
      element, 
      {
        html: true,
        sanitize: false,
        trigger: 'manual',
        content: 'Nice',
        offset: [0, offsetDistance]
      }
    );
    return popover;
} 
  const newPopover = generatePopover(targetElement);
  newPopover.toggle();
}

function togglePopover_toMouse(event) {
  
  //Dispose of any existing popover for this element.
  const oldPopover = bootstrap.Popover.getInstance(event.target);
  if (oldPopover){
      oldPopover.dispose();
  }
  
  const generatePopover_toMouse = (event) =>{
    // Get the clicked element
    const targetElement = event.target;

    // Set up the popover 
    const popover = new bootstrap.Popover(
      targetElement, 
      {
        html: true,
        sanitize: false,
        trigger: 'manual',
        content: 'Nice'
      }
    );

    // Obtain the clicked element's boundRect
    const boundingRect = targetElement.getBoundingClientRect();
    const x = event.clientX - boundingRect.left; //x position within the element.
    const y = event.clientY - boundingRect.top;  //y position within the element.

    // Set up an event listener to move the popover after it is shown
    targetElement.addEventListener('shown.bs.popover',() => {
      if (popover.tip){
        popover.tip.style.left = `${x - boundingRect.width}px`;
        popover.tip.style.top = `${y - (boundingRect.height/2)}px`;
      }
    });

    popover.toggle();
  } 

  // Create a new popover by passing in the click event
  generatePopover_toMouse(event);
}
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="52303d3d26212620332212677c627c60">[email protected]</a>/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="54363b3b20272026352414617a647a66">[email protected]</a>/dist/css/bootstrap.min.css" rel="stylesheet"/>

<svg onclick="togglePopover_toCenter(event)"  height="100" width="100">
  <circle id="test" cx="50" cy="50" r="40" stroke="black" stroke width="3" fill="red" />
</svg>

<br />

<svg onclick="togglePopover_toMouse(event)"  height="100" width="100">
  <circle id="test" cx="50" cy="50" r="40" stroke="black" stroke width="3" fill="blue" />
</svg>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Scrolling seamlessly on websites with a single page

I am developing a single-page website that uses different section IDs instead of multiple pages. It's functioning properly, but I want to implement smooth scrolling to the sections rather than just statically jumping to them on the page. Jsfiddle: ht ...

Discover the power of integrating JSON and HTTP Request in the Play Framework

My aim is to develop a methodology that can effectively manage JSON and HTTP requests. This approach will facilitate the transition to creating both a Webapp and a Mobile App in the future, as JSON handling is crucial for processing requests across differe ...

Tips for incorporating conditions when updating data in MongoDB

I am looking for assistance with updating the secondary phone number in the code below. I want to update it only if a 10-digit number is passed from the web form; otherwise, I would like to use the already inserted phone number during the insert operation. ...

Update current properties of objects

I'm feeling like I'm going crazy and could really use some assistance. My predicament involves a function that looks like this: private generateTimeObject(firstObject: someInterface, secondObject?: someInterface) { let firstTime; let secondTi ...

pause in execution between iterations of a for loop

Challenge I'm currently working on a function that processes a list of strings by printing each one on a new line, with CSS animations that gradually display the string over a few seconds. However, I'm struggling to make sure that the next strin ...

DANGEROUS EVALUATION: Tips for Safe Replacement

Looking for a safer alternative to the code below which utilizes eval. The script creates pop-up windows based on different classes. /* exported popup_default , popup_help , popup_sitemap , popup_footerlinks */ var matchClass = ['popup_default' ...

The submission of the form is not functioning correctly when triggered by JavaScript using a button

My website was designed using a CSS/HTML framework that has been seamlessly integrated into an ASP.NET site. Within a ContentPlaceHolder, I have implemented a basic login form. The unique aspect is that I am utilizing the onclick event of an image to subm ...

Adjust the height of the dropdown list in react-select (React Material UI)

I recently added a dropdown feature using react-select to my project. However, I encountered an issue where the dropdown list was initially too large and taking up the entire page. I am looking for a solution on how to style the react-select dropdown list ...

Update the value of a Vue tree select component using JavaScript

I'm working on a school project using plain JavaScript and needed a tree view select with multiple layers. After extensive searching, I stumbled upon this tool. It's working smoothly, but the one thing that has me stumped is how to change its va ...

Include token in src tag requests Angular version 8

In the process of developing a website, I have encountered a challenge. I am creating a platform where users can access another website I am currently working on after they log in. Once authorized, users receive a JWT token which is sent in the header with ...

Placing a materialUI circular progress bar on top of the circular icon

I'm striving to add a circular progress bar that surrounds a circular icon with a visible space between the two elements, just as depicted in the image below. Although I successfully positioned the circular progress bar around the icon, creating adequ ...

Utilizing Fancybox with a polymer paper-card: A step-by-step guide

I have a collection of paper-cards and I am looking for guidance on integrating the fancybox library to display the content of each paper-card. Here is a sample of a paper-card: <paper-card heading="Emmental" image="http://placehold.it/350x150/FF ...

Tips on maintaining the content of an element within a directive template

I'm currently facing an issue with adding the ng-click directive to a button. Here's my HTML: <button class="btn">clicky</button> This is the directive I am using: angular.module('app').directive('btn', function ...

Aligning elements in the center vertically using absolute positioning for multiple elements

I have been experimenting with a method to vertically center an element in a div that has unknown height. You can check out the approach I used here: In my case, I am working with anchor tags and this solution was particularly helpful due to the position ...

What is the process for converting the output of cryptoJS.sha256 to binary in a Postman pre-request script?

Seeking assistance in creating an HMAC signature using a pre-request script in Postman. While troubleshooting, it has become apparent that there is an issue with the signature generation process. Although a proof of concept example provides expected result ...

The 'fs' module does not seem to have an immediate impact; a server restart may be necessary for the changes to take

This NodeJS project involves starting the server with npm start. The project reads files from a folder called "./mydir/" using 'fs.readdirSync'. It then pushes these files into an array and prints them on the console. var fs = require('fs ...

"Using jQuery to append a new div after the last div that matches a specified class on

I am looking to dynamically add a new div element at the end of the last match on click. <form id="form"> <div class="border item-block"> <p><b>Color :</b> silver </p> <input type= ...

transforming a text input into unadorned plain text

Currently, I am in the process of creating a small HTML form that consists of a single textbox input. Once the user enters text into this textbox and clicks on a button located at the end of the page, I would like the textbox to transform into normal plain ...

What is the best way to show the page before loading custom fonts?

My website is time-sensitive and I need it to prioritize displaying the page before loading any custom fonts. Essentially, I want the page to show up initially as if the fonts are not loaded. This way, the custom fonts (if already cached by the browser) w ...

Establish a secure connection to MySQL through SSH tunneling with node-mysql

Is it feasible to establish a connection to the MySQL server using an SSH key instead of a password when utilizing the node-mysql npm package? ...