Search code examples
htmlreactjscreate-react-appmasonry

Trying to get an overlay to toggle on a repeating element. Can't use getElementByID


As per the title I want an overlay to trigger when an image is clicked on, but I then want it to disappear if anywhere other than 3 buttons on the overlay are clicked.

Unfortunately using getElementbyID won't work as the items repeat on a masonry layout.

    <Masonry
    breakpointCols={breakpointColumnsObj}
    className="my-masonry-grid"
    columnClassName="my-masonry-grid_column">
      
      {this.state.data.map((data) => (
        <div>
          <div className="tilebutton" key=""  style={{width:'100%',position:'relative'}} href={data.URL} >
              <div className="tileoverlay" id="overlay" onClick={overlayoff} onclickout key={data.URL} style={{display:'none',width:'100%',zindex:'2',position:'absolute'}}>
                <a className="button1" href={data.URL} onClick>{data.Name}</a>
                <a className="button2" href={data.CompURL}>{data.Company}</a>
                <a className="button3" href={'instagram.com/'+data.insta}>{data.Company}<img src="\img\Icons\instagram.svg" className='instalogo'/></a>
              </div>
              <img src={data.img} onClick={overlayon} style={{width:'100%'}}/>
          </div>
        </div>
      ))}
    </Masonry>
  )
  
function overlayon() {
  document.getElementById("overlay").style.display = "block";
}

function overlayoff() {
  document.getElementById("overlay").style.display = "none";
}

Unfortunately using the id "overlay" means if I click any version of the masonry it will trigger the overlay on the first image. Is there some way to:

a) identify the element clicked so it will be the one with the toggling overlay

b) have an "onclickout" I could apply to the overlay's buttons

this is about 5 days into my first ever web build so frankly I haven't got a clue what I am doing - any help is appreciated.

Thanks in advance.


Solution

  • The idioms used in React discourages you to manipulate the DOM directly, unless you are doing something special, such as animation. And thus I don't recommend "identifying the element clicked".

    With that said, you can manipulate the data, and trigger a redraw accordingly, by invoking some setSate function (in the example below, I've defined a setShouldShowOverlay, that, when invoked, will result in a redraw).

    What I recommend is for you to pull out the code inside this.state.data.map() into its own component, like so:

    import React, { useState } from 'react';
    
    function Data({ data }) {
      const [ shouldShowOverlay, setShouldShowOverlay ] = useState(false);
    
      return (
        <div>
          <div className="tilebutton" key=""  style={{width:'100%',position:'relative'}} href={data.URL} >
              <div className="tileoverlay" id="overlay" onClick={() => { setShouldShowOverlay(false); }} onclickout key={data.URL} style={{display:'none',width:'100%',zindex:'2',position:'absolute'}}>
                <a className="button1" href={data.URL} onClick>{data.Name}</a>
                <a className="button2" href={data.CompURL}>{data.Company}</a>
                <a className="button3" href={'instagram.com/'+data.insta}>{data.Company}<img src="\img\Icons\instagram.svg" className='instalogo'/></a>
              </div>
              <img src={data.img} onClick={() => {
                setShouldShowOverlay(true);
              }} style={{width:'100%'}}/>
          </div>
        </div>
      );
    }
    

    Then finally, update your Masonry code like so:

    <Masonry
      breakpointCols={breakpointColumnsObj}
      className="my-masonry-grid"
      columnClassName="my-masonry-grid_column">
        
        {this.state.data.map((data) => (
          <Data data={data} />
        ))}
    </Masonry>