What is the best way to move a card when it appears on hover?

I recently implemented a map with interactive cards that appear when hovering over hotspots, all thanks to the amazing support I received on this platform. However, I have encountered a problem where the card appears to the right of the hotspot and gets cut off when the hotspot is near the right edge of the browser window. The setup involves a combination of CSS and JavaScript, with my expertise leaning more towards CSS than JavaScript.

I've tried adjusting the CSS positioning, but it didn't solve the issue. I suspect that there might be a need to modify something in the JavaScript code instead?

You can experience the problem on the webpage by hovering over number 35 or 36.

Here is the snippet of CSS code:

#card {
    position: relative;
    left: 30px;
    width: 300px;
    height: 150px;
    display: none;
    border: none;
    background: #ffd;
    pointer-events: none;
}

And here is the corresponding JavaScript code:

let paths = document.querySelectorAll("path");
paths.forEach((p) => {
    p.addEventListener("mouseleave", (evt) => {
        card.style.display = "none";  
    });
    p.addEventListener("mousemove", (evt) => {
    let pos = oMousePos(svg, evt);
    let text = p.dataset.text;

    card.style.display = "block";
    card.style.top = pos.y + "px";
    card.style.left = pos.x + "px";
    card.innerHTML = text;
    });

});

function oMousePos(element, evt) {
    let ClientRect = element.getBoundingClientRect();
    return {
    //object
    x: Math.round(evt.clientX - ClientRect.left),
    y: Math.round(evt.clientY - ClientRect.top)
    };
}

Answer №1

To ensure that the current position of the cursor in terms of x and y coordinates, as well as the width of the card/popup, does not surpass the boundaries of your svg, follow these steps:

function determineCursorPosition(element, event) {
  let clientRect = element.getBoundingClientRect();
  let currentX = Math.round(event.clientX - clientRect.left);
  let currentY = Math.round(event.clientY - clientRect.top);

  // Check if close to the right edge
  if (event.clientX + cardWidth >= clientRect.right) {
    currentX = Math.round(event.clientX - clientRect.left - cardWidth);
  }
  // Check if close to the bottom edge
  if (event.clientY + cardHeight >= clientRect.bottom) {
    currentY = Math.round(event.clientY - clientRect.top - cardHeight);
  }
  return {
    x: currentX,
    y: currentY
  };
}

If the cursor is approaching the right edge (evt.clientX + cardWidth), you may need to apply a negative vertical offset equal to the width of the popup (300 in this case). The same logic applies to vertical overflow situations.

Here is a simplified example:

    let paths = document.querySelectorAll("path");
    let cardWidth = card.getBoundingClientRect().width;
    let cardHeight = card.getBoundingClientRect().height;

    paths.forEach((p) => {
      p.addEventListener("mouseleave", (evt) => {
        card.style.visibility = "hidden";
      });
      p.addEventListener("mousemove", (evt) => {
        let position = determineCursorPosition(svg, evt);
        let text = p.dataset.text;
        card.style.visibility = "visible";
        card.style.top = position.y + "px";
        card.style.left = position.x + "px";
        card.innerHTML = text;
      });
    });
 #card {
      position: absolute;
      width: 33%;
      height: 10vmin;
      /* display: none;*/
      visibility: hidden;
      border: none;
      background: #ffd;
      pointer-events: none;
    }

    svg{
      width:90%;
    }

    svg path {
      fill: red !important;
      opacity: 0.5 !important;
    }
<svg id="svg" viewBox="0 0 2250 1800" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"><g data-groupmode="layer" data-label="Image" id="g448">
  <rect x="0" y="0" width="100%" height="100%" fill="#ccc"></rect>
  <a xlink:href="http://travelalberta.com/"><path d="m1922 187-8-68 132 12 29 20 4 36-99 12z" id="path1336" data-text="&lt;h4&gt;Bobs Boquets&lt;/h4&gt;&lt;h5&gt;23rd street NW Flower Ave&lt;/h5&gt;&lt;h6&gt;Next to Widget World1&lt;/h6&gt;"/></a><path d="m2088 420-22-18 14-22 70-5 26-31 25 4-7 21 24 14 3 30-17 11-25 5-57 10-22-6z" id="path1338" data-text="&lt;h4&gt;Bobs Boquets&lt;/h4&gt;&lt;h5&gt;23rd street NW Flower Ave&lt;/h5&gt;&lt;h6&gt;Next to Widget World2&lt;/h6&gt;"/><path d="m1841 306-165-70 4-47 151 75z" id="path1334" data-text="&lt;h4&gt;Bobs Boquets&lt;/h4&gt;&lt;h5&gt;23rd street NW Flower Ave&lt;/h5&gt;&lt;h6&gt;Next to Widget World3&lt;/h6&gt;"/><path d="M2111 1445s45 0 48-2c4-2 30-31 30-31l5-31-37-33-49 2-27 64v38z" id="path2293"/><path d="m1600 1658 41 5 60-41v-31l-46-36-56-4-21 35z"...
  </g>
</svg>
  <div id="card"></div>

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

Implementing pagination in Firestore using React-Redux

I'm currently working on implementing pagination with Firebase and React Redux Toolkit. I've grasped the logic behind it, but I'm facing challenges when integrating it with Redux. Initially, my approach was to store the last document in the ...

Tips for transferring variables as arguments in javascript

Currently, I am immersed in a test project aimed at expanding my knowledge of web development. Unexpectedly, I have encountered an issue that has left me puzzled on how to proceed. Within this project, there exists a table consisting of 11 cells. While 9 ...

Is there a way to highlight today's working hours with a different color using Vue.js?

Here is the script I have created to display the working hours: const workHour = "Monday :9:00AM to 5:00PM,Thursday :9:00AM to 5:00PM,Wednesday :9:00AM to 5:00PM,Tuesday : 9:00AM to 5:00PM,Friday :9:00AM to 5:00PM,Saturday :9:00AM to 5:00PM,Sunday :9:00AM ...

Leverage the power of Angular to seamlessly integrate jQuery code across your

Interested in incorporating the plugin into my AngularJS project. To achieve this, I have to: $(document).ready( function() { $("html").niceScroll(); } ); The challenge is figuring out where to place this code so that it runs site-wide and ...

Guide on developing and releasing a Vuejs component on NPM

I have been diving deep into vue lately and incorporating it into all of the projects at my workplace. As a result, I have developed various components, particularly autocomplete. Although there are plenty of existing options out there, none seem to fulfil ...

Subtracting points in a three-dimensional JSON file using three.js

I have a file in three.js json format that contains a model exported from Blender using BufferGeometry, along with its corresponding texture file. The model has some unwanted noise on it that I am aiming to clean up by manipulating the json file itself af ...

If the storeAlertPresent condition is not true in Selenium IDE, then proceed to the next

I am facing a challenge with testing an insurance policy admin system that involves entering policy holder information using a custom extension that generates random individuals, including vehicle VINs pulled from Polk verified VINs. The system I am creati ...

Can one validate a single route parameter on its own?

Imagine a scenario where the route is structured as follows: companies/{companyId}/departments/{departmentId}/employees How can we validate each of the resource ids (companyId, departmentId) separately? I attempted the following approach, but unfortunate ...

Fetching data from local JSON file is being initiated twice

I need some help understanding why my code is downloading two copies of a locally generated JSON file. Here is the code snippet in question: function downloadJson(data, name) { let dataStr = 'data:text/json;charset=utf-8,' + encodeURICompo ...

In what way is the reference to "this" passed in the function that follows?

I came across the following example in the JS manual, which is functioning correctly: let worker = { someMethod() { return 1; }, slow(x) { alert("Called with " + x); return x * this.someMethod(); // (*) } }; function cachingDecorator ...

Explore the extensive JSON link for redirecting

I have an issue with accessing the HATEOS link example on PayPal. My browser is showing a syntax error when I try to access the link. SyntaxError: missing ) after argument list [Break On This Error] alert(links.1.href); (line 32, col 15) The JSON d ...

Using jQuery to bind the "CTRL+A" key combination to exclusively selecting a specific region

My goal is to modify the CTRL+A shortcut so that instead of selecting all text, it will only select specific content within containers with a class of xyz. Unfortunately, I have not been successful in getting this functionality to work. Even attempting to ...

How do I retrieve distinct values from Math.random in JavaScript and prevent them from repeating?

I am attempting to fetch HTML dom elements remotely from a website using jquery/javascript. These elements are stored in an array and are unique, however, when I attempt to display them by generating random indexes with math.random() * my array.length, I n ...

What methods can be used to pause a jQuery function when hovering and resume it when the mouse leaves?

I'm currently working on a jQuery function that operates on the mousemove event. The goal is to have two images - one main image and one shadow image, with the shadow image reflecting movement based on mouse position. While everything is functioning p ...

The type '(dispatch: Dispatch<any>, ownProps: OwnProps) => DispatchProps' does not match the parameter type 'DispatchProps'

Currently, I am working on a React application using Redux and TypeScript. I came across this insightful article that provided guidance on creating types for the mapStateToProps and mapDispatchToProps functions. Below is the code for my container: import ...

Is it possible to use a Proxy-object instead of just an index when changing tabs in material-ui/Tabs?

Using material-ui tabs, I have a function component called OvertimesReport with Fixed Tabs and Full width tabs panel: const TabContainer = ({children, dir}) => ( <Typography component="div" dir={dir} style={{padding: 8 * 3}}> {children} & ...

Setting the default option in an Autocomplete component using Material UI in React JS

I am currently working with autocomplete input using react material ui component. One specific challenge I am facing is setting a default selected value when a user enters edit mode for this input field. Although I can select the option using getOptionSe ...

Creating a hierarchical tree structure from tabular data with JavaScript ECMAScript 6

Seeking a JavaScript algorithm utilizing ECMAScript 6 features to efficiently convert JSON table data into a JSON tree structure. This approach should not employ the traditional recursive algorithm with ES5, but rather emphasize a functional programming me ...

Embarking on the journey of transitioning code from server-side to client-side

Currently, I am looking to transition the code behind section of my asp.net web forms application to client-side ajax or javascript - still deciding on which route to take. The main goal for this change is to ensure that the application remains functional ...

Ever since updating my Node JS, the functionality of the MaterializeCSS carousel methods has ceased to work

Recently, I encountered some issues with my React project that features a materialize-css carousel. The problem arose after updating my nodeJS version from 14.8.1 to 16.13.0. Specifically, these errors kept popping up: TypeError: Cannot read properties o ...