I'm building a wordpress block using react (in the frontend).
The block will initially fetch a list from a server, then display the list using map
.
For each item, it will render a header, like so:
As you can see, you can click a header and the content shows up. This content is build in the same map
and they have their max-height
and opacity
set to 0
to hide them. I'm using those properties instead of just display: none
, because I can CSS-transition them and it looks smoother. I had to blur the content, but you get the idea.
However, in some of the content-divs, the buttons are not working. The links-stlyed-as-buttons (the music services on the left) do not behave as links, although they have their href
set in the markup. The hover effects do not work either, the cursor does not become the pointy finger and they are not clickable.
A div styled as a play-button has the same issues: in eventListeners I can see the onClick
being listened for, but the button is not clickable, has no hover, etc.
One noteable thing is: it's not all list entries behaving this way. And if one list entry with broken buttons is open, and you open another content section with working buttons, the broken buttons will magically start working. If you then close the content section with working buttons, the formerly broken ones are broken again.
Honestly, this feels like my code is haunted.
Please note: because the length and content of the list is not entirely known, I have to use generated IDs and generated getElementByID
-queries to interact with the items and can't define states for them (at least I couldn't think of a way to dynamically create states).
React:
//...
//releases is useState([]) of an object with metadata of songs
//comes from a database query, so the columns are known
{releases.map(release => {return(
<div key={release.catalogue_index} style={{marginTop: "8px"}}>
{/*This is the "header", shown in the screenshot*/}
<div className="layout-row" style={{backgroundColor: "#111", padding: "8px", marginTop: "4px", position: "relative"}} onClick={(event) => {
document.getElementById(`${release.catalogue_index}-content`).classList.toggle('open');
}}>
<div stlye={{flexGrow: 1}} className="layout-stack">
{/* here the title and some information is displayed*/}
</div>
</div>
{/*The content you can show (second screenshot)*/}
<div className="release-content" id={`${release.catalogue_index}-content`}>
{/*layout-stack and layout-row are just flex containers with different directions*/}
<div className="layout-row">
<img loading="lazy" alt={`${release.title} cover`} src={/*cover.jpg*/}
className="release-cover" />
<div className="layout-stack">
<div>{/*some metadata*/}</div>
<hr />
{release.short_description}
<div style={{height: "16px"}} />
<div className="layout-row">
{/*those are the link buttons sometimes not working*/}
{/*the whole thing only gets displayed if any link got fetched from the database*/}
{(release.link_bandcamp || release.link_youtube || release.link_spotify || release.link_apple) ?
<div className="layout-row">
<p className="link-caret"><strong>></strong></p>
<ul className="is-content-justification-center is-layout-flex wp-container-9 wp-block-social-links has-icon-color has-icon-background-color featured-social-group" style={{margin: 0}}>
{release.link_bandcamp ?
<li style={{color: "#fff", backgroundColor: "#888"}} className="wp-social-link wp-social-link-bandcamp wp-block-social-link wp-custom-social">
<a target="_blank" href={release.link_bandcamp} className="wp-block-social-link-anchor" rel="noopener"><img className="svgbutton" src={/*some-logo.svg*/} />
<span className="wp-block-social-link-label screen-reader-text">Bandcamp</span></a></li>
: null}
{release.link_youtube ?
<li style={{color: "#fff", backgroundColor: "#888"}} className="wp-social-link wp-social-link-youtube wp-block-social-link wp-custom-social">
<a target="_blank" href={release.link_youtube} className="wp-block-social-link-anchor" rel="noopener"><img className="svgbutton" src={/*some-logo.svg*/} />
<span className="wp-block-social-link-label screen-reader-text">Youtube</span></a></li>
: null}
{release.link_spotify ?
<li style={{color: "#fff", backgroundColor: "#888"}} className="wp-social-link wp-social-link-spotify wp-block-social-link wp-custom-social">
<a target="_blank" href={release.link_spotify} className="wp-block-social-link-anchor" rel="noopener"><img className="svgbutton" src={/*some-logo.svg*/} />
<span className="wp-block-social-link-label screen-reader-text">Spotify</span></a></li>
: null}
{release.link_apple ?
<li style={{color: "#fff", backgroundColor: "#888"}} className="wp-social-link wp-social-link-chain wp-block-social-link wp-custom-social">
<a target="_blank" href={release.link_apple} className="wp-block-social-link-anchor" rel="noopener"><img className="svgbutton" src={/*some-logo.svg*/} />
<span className="wp-block-social-link-label screen-reader-text">Apple Music</span></a></li>
: null}
</ul>
</div> : null}
<audio id={`audio-${release.catalogue_index}`} preload="none"
onEnded={() => {document.getElementById(`audio-seek-${release.catalogue_index}`).value = 0}}
onl oadedMetadata={() => document.getElementById(`audio-seek-${release.catalogue_index}`).max = document.getElementById(`audio-${release.catalogue_index}`).duration}
onTimeUpdate={() => document.getElementById(`audio-seek-${release.catalogue_index}`).value = Math.floor(document.getElementById(`audio-${release.catalogue_index}`).currentTime)}>
<source src={/*REDACTED*/} type="audio/mpeg" />
Your browser does not support audio.
</audio>
<div className="layout-row" style={{alignItems: "center"}}>
{/*this is the play button sometimes acting up*/}
<div className="play-pause wp-block-button"
onClick={() => {togglePlayPause(release.catalogue_index)}}>
<img id={`audio-button-${release.catalogue_index}`} className="play-pause svgbutton wp-block-button__link wp-element-button" src={/*play-svg*/} />
</div>
<input type="range" id={`audio-seek-${release.catalogue_index}`} className="audio-seek" max={100} value={0} />
</div>
</div>
</div>
</div>
<div style={{height: "16px"}} />
<div className="layout-stack">
{/*some flavor text*/}
</div>
</div>
</div>
)})}
CSS:
//...
.release-content {
max-height: 0vh;
opacity: 0;
transition: max-height 0.3s;
transition: opacity 0.3s;
}
.release-content.open {
opacity: 1;
height: fit-content;
padding: 16px;
max-height: 100vh;
}
.svgbutton {
color: currentColor;
fill: currentColor;
width: 1em;
height: 1em;
}
div.play-pause {
margin-left: 36px !important;
height: 38px;
}
img.play-pause {
box-sizing: unset !important;
margin-top: 0 !important;
}
//...
The only thing I could think of was that maybe the wordpress-classes ("wp-...") would somehow break ... but that doesn't make much sense, as some of the the list entries work fine while others don't - all using those same classes.
CodePudding user response:
Based on RubenSmn's comment, I had to realize that I did not plan my code well and built terrible anti-patterns.
I have refactored the code to not map into all that markup, but a separate <Release />
component, which in turn contains the markup.
{releases.map(release => {return(
<ReleaseComponent key={release.catalogue_index} release={release} />
)})}
The new component was then built to not use classList.toggle
, but proper React states to show and hide the content panels. Also the document.getElementByID
-queries are gone and replaced by proper React refs.
const ReleaseComponent = (props) => {
const contentDiv = useRef(null);
const audioElement = useRef(null);
const audioButton = useRef(null);
const audioSeek = useRef(null);
const [contentOpen, setContentOpen] = useState(false);
//...
return(
//...
<div className="release-content" ref={contentDiv} hidden={!contentOpen}>
//...
Turns out, if you do things right, they actually work... This was a pretty humbling experience. Thank you, @RubenSmn .