Crafting an interactive SVG element that maintains its clickability without interfering with mouseLeave events

Trying to Achieve:

  • Changing color and displaying SVG when hovering over a row
  • Clicking the SVG triggers a function, including external functions (not limited to ones defined inside script tags)
  • Returning the row to its original form when mouse leaves

Challenges Faced:

  • Hovering over the SVG button causes a mouseLeave event (row returns) unless pointer-events are set to none
  • onClick inside SVG does not seem to support external functions (such as props functions)

Seeking assistance to address these issues.

const { Container, Row, Col} = ReactBootstrap;
function Library(props){
  
  function handleMouseEnter(e){
    e.style.background = 'blue';
    var targetNode = e.querySelector(`#content`);
    ReactDOM.render(                
      <svg  width="0.75rem" height="0.75rem" className="Layer_1" x="0px" y="0px" viewBox="0 0 472.615 472.615" fill="white">   
        <polygon onClick={e => console.log('clicked')} points="50.273,0 50.273,472.615 422.342,236.308"/>
      </svg>
      , targetNode);
  }
  
  function handleMouseLeave(e){
    e.style.background = '';
    var targetNode = e.querySelector(`#content`);
    ReactDOM.render(                
      "hover over me and click the button!"
      , targetNode);
  }
  
  return(
  <Container>
    <Row 
    onMouseEnter={e => handleMouseEnter(e.target.closest('.row'))}
    onMouseLeave={e => handleMouseLeave(e.target.closest('.row'))}
    >
      <Col>
        <div id="content">
          hover over me and click the button!
        </div>        
      </Col>
    </Row>
  </Container>
  )
}

ReactDOM.render(
  <Library/>,
  document.getElementById('root')
);
.row{
background:red;
}
svg{
pointer-events:none;
}
<script src="https://unpkg.com/react/umd/react.production.min.js" crossorigin></script>

<script
  src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"
  crossorigin></script>

<script
  src="https://unpkg.com/react-bootstrap@next/dist/react-bootstrap.min.js"
  crossorigin></script>
<div id="root"></div>

Answer №1

To include an SVG file in your project, you can create a file with the extension .svg and save your SVG code in that file. For example:

Icon.svg

<svg width="0.75rem" height="0.75rem" className="Layer_1" x="0px" y="0px" viewBox="0 0 472.615 472.615" fill="white">   
    <polygon points="50.273,0 50.273,472.615 422.342,236.308"/>
</svg>

To display the SVG icon in your component, you can import the Icon.svg file like this:

YourComponent.jsx

import { ReactComponent as Icon } from "./Icon.svg";

const YourComponent = () => {
  return (
    <div>
      <Icon onClick={e => console.log('clicked')} />
    </div>
  );
};

export default YourComponent;

Congratulations! You can now click on your SVG icon. It is recommended to import SVG images using the ReactComponent format for best practices.

Answer №2

Perhaps you could experiment with utilizing the useState hook for handling conditional rendering in this scenario

import { useState } from "react";
import { ReactComponent as Icon } from "./Icon.svg";

const CustomApp = () => {
  const [isHovering, setIsHovering] = useState(false);

  const handleMouseEnter = () => {
    setIsHovering(true);
  };
  
  const handleMouseLeave = () => {
    setIsHovering(false);
  };

  return (
    <div
      style={isHovering ? { background: "blue" } : { background: "red" }}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
    >
       {isHovering && <Icon onClick={(e) => console.log("clicked")} />}
      Hover over me and click the button!
    </div>
  );
};

export default CustomApp;

Answer №3

I have successfully resolved the issue by making a modification in the onMouseLeave function to verify whether the element that triggered it was an SVG or one of its children. If so, I now classify it as a false positive. As a result, the button remains clickable and does not disappear when hovered over.

const { Container, Row, Col} = ReactBootstrap;
function Library(props){
  
  function handleMouseEnter(row){
    row.style.background = 'blue';
    var targetNode = row.querySelector(`#content`);
    ReactDOM.render(                
      <svg onClick={e => console.log('clicked')} width="0.75rem" height="0.75rem" className="Layer_1" x="0px" y="0px" viewBox="0 0 472.615 472.615" fill="white">   
        <polygon points="50.273,0 50.273,472.615 422.342,236.308"/>
      </svg>
      , targetNode);
  }
  
  function handleMouseLeave(e, row){
    if(e.relatedTarget.tagName === 'svg' || e.relatedTarget.tagName === 'polygon') return
    row.style.background = '';
    var targetNode = row.querySelector(`#content`);
    ReactDOM.render(                
      "hover over me and click the button!"
      , targetNode);
  }
  
  return(
  <Container>
    <Row 
    onMouseEnter={e => handleMouseEnter(e.target.closest('.row'))}
    onMouseLeave={e => handleMouseLeave(e, e.target.closest('.row'))}
    >
      <Col>
        <div id="content">
          hover over me and click the button!
        </div>        
      </Col>
    </Row>
  </Container>
  )
}

ReactDOM.render(
  <Library/>,
  document.getElementById('root')
);
.row{
background:red;
}
<script src="https://unpkg.com/react/umd/react.production.min.js" crossorigin></script>

<script
  src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"
  crossorigin></script>

<script
  src="https://unpkg.com/react-bootstrap@next/dist/react-bootstrap.min.js"
  crossorigin></script>
<div id="root"></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

The name 'Promise' appears to be missing

I've run into a problem when trying to run react-xp for the web. The error message "Cannot find name 'Promise'" keeps popping up. Any help would be greatly appreciated. For more details, please refer to the Github issue react-xp issue ...

Modifying HTML elements with JavaScript - a practical guide

I'm trying to dynamically add the variable x to an existing HTML tag. The goal is to update the image tag <img id="Img" src="IMG/.jpg"/> by appending the variable x at the end of its id and source: <script> var images ...

Waiting with Protractor's browser.wait can lead to Angular timeouts

I've been working on this code snippet: browser.sleep(5000).then(function() {console.log('rotel:' + browser.rootEl)}); browser.ignoreSynchronization = true; browser.rootEl = 'div#overview'; browser.driver.switchTo( ...

Tips for including markdown content within components in MDX files

I'm currently in the process of utilizing MDX pages along with React components for a project. My issue lies in wanting to generate HTML when adding text inside a component, similar to how it works with content outside of components. However, I'v ...

Determining the Existence of a Model in Backbone/Marionette

I've built a simple backbone application, but I'm struggling with a more complex check that needs to be performed. Below is my code. I'm creating a list of chat participants. Eventually, I'll pass this list into a JavaScript function. ...

When using Chart JS, is there a way to create a line graph without including any labels at all?

Currently, I am facing a challenge with my Chart JS line graph. It needs to pull data from a backend and display it on a webpage. However, the chart has close to 1000 points to plot, making it impossible for me to provide labels for each point on both the ...

Having trouble with spawning child processes asynchronously in JavaScript

I'm trying to figure out how to format this code so that when a user clicks a button, new input fields and redirect buttons are asynchronously inserted into the unordered list. Everything was working fine until I added the redirect button insertion fu ...

Error in AJAX POST: base64 string formatting issue

Struggling with making an AJAX POST successfully upload and retrieve a base64 string to/from my SQL database. Upon receiving the string from the database via AJAX, it appears to be the same base64 string, but with random line breaks that render it non-func ...

The PhpStorm/JavaScript expression statement doesn't involve assignment or function call

I am striving to enhance the cleanliness of my method. Based on the value of an integer number, I am generating different date formats, resulting in the following: getRanges() { var currentDate = new Date(); //This can ...

Choose the child nodes upon selecting the root node

I am trying to implement a 2-level material-ui treeview where selecting the root node should automatically select all child nodes as well. Does anyone have suggestions on how to achieve this with material-ui treeview? For more information, please visit ma ...

Generating dynamic @returns annotations in JSDoc based on the value of @param

How can I properly document the function in order for vscode-intellisense to recognize that getObject("player") returns a Player type and getObject("bullet") returns a Bullet type? /** * @param {string} type * @return {????} */ function getObject(type ...

Next/image is encountering an error due to an invalid Element type being generated

Trying to utilize the next/image feature to display an SVG image is causing me some trouble. Every time I attempt this, an error message pops up: Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite ...

Can you create a dynamic visual display using HTML5 Canvas to draw lines in a circular pattern that react

I have successfully implemented drawing lines around a circle using the AudioContext API. However, I am facing an issue with the lineTo function, as the line only grows and does not shrink. My inspiration for this project comes from the audio visualizer fo ...

Is there a way for me to update the value of an element within an object using extrareducer in Redux?

This represents the starting point: const initialState ={ ordersWholesale:[ { "id": 14, "name": "XTPara 650mg Tablet", "code": "XTP5656", "date": "17/10/2022", "accepted": null, "wholesale": "shakthi", "quant ...

How do I access the device manager in Android Studio when working with React?

I am new to Android development and React. After installing Node.js and creating a project with React, I attempted to open it in Android Studio. However, when trying to access the device manager that was previously available, I encountered an issue where I ...

The Dojo claro css method utilizes absolute positioning for styling ContentPane elements

I am currently utilizing dojo 1.8 and facing an issue with unwanted padding in my bordercontainer/contentpane layout. The problem arises when I incorporate the claro css file, as it seems to apply styles directly inline to the div elements used for my cont ...

Converting TypeScript to JavaScript: A Step-by-Step Guide

I have this code written in Typescript and I need to convert it to JavaScript const Home = (props) => { return ( <div> {props.name ? 'Hi ' + props.name : 'You are not logged in'} </div> ); }; How can I re ...

Can I customize the color of an SVG image with CSS (jQuery SVG image substitution)?

This is my innovative solution for easily embedding and styling SVG images using CSS. Traditionally, accessing and manipulating SVG elements with CSS has been a challenge without the use of complex JS frameworks. My method simplifies this process, making ...

Using the `ng-if` directive in Angular to check for the

I need to output data in JSON format using items. To display a single item, I utilize ng-repeat="item in items". Additionally, I can access the user object of the currently logged-in user with user. Every item has the ability to belong to multiple wishlis ...

What is the best method for asynchronously injecting and providing data?

Within my page, I have implemented an asynchronous fetch method to initialize data: async fetch() { const res = await requestApi(this, '/database'); this.sliderData = res.homeSlider; this.modelData = res.model; ... } To pass thi ...