Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
May 19, 2022 08:24 pm GMT

React: Using native dialogs to make a modal popup

Around two years ago I wrote a post, that I'm still very proud of, called "Using portals to make a modal popup". It made use of portals and inert. At the time there wasn't really a good way to display a modal popup on top of everything else and trap focus within it.

Two years have passed since the original post and, whilst I would argue the code is still valid and the techniques used are still worth knowing, there is now a better way to code a modal. The dialog element has been around since 2014ish but it has only recently reached full browser compatibility (excusing IE).

Let's look at how we can use the dialog element to make this.

The Dialog Element

What is the Dialog Element? The <dialog> element is a native html element made with the sole purpose of being popover content. By default the contents of the element are hidden but by setting the open attribute to true or by calling either of its show methods show() or showModal() they can be revealed.

It is not, however, best practice to set the open attribute directly, even though it is possible, but rather calling a show method is preferred. show() makes the dialog appear but leaves the rest of the page interactable this is great for making toast notifications. showModal() opens the dialog in the centre of the screen and makes all other content inaccessible.

can I use for dialog

What are the advantages of using it? I'm sure there are numerous reasons to use dialog over making your own solution but I'll focus on three.

  • Native elements do not require large libraries or imports, speeding up your app.
  • Accessibility is handled for you, when a browser ships an element it is built to a11y standards.
  • The logic is pretty much there, you don't have to work out how to make the interactions happen they just work.

Are there any drawbacks? Yes. Well, sort of. The element does not have animations built in and uses display: none which is famously hard to animate from. It also lacks, strangely in my opinion, a close method.

These issues almost feel like deal breakers but there are ways around them both, which I will show as we go on.

The general component

I'm not going to spend too long going through the code I've written, feel free to read it and ask questions in the comments, but I'll give a quick run down here and then explain my solutions to issues I mentioned above.

First of all I'm using Sass and CSS Modules if you've seen some of my earlier posts you'll have seen I used to use styled-components and whilst I think they have their place I'm much happier using CSS Modules.

The code does a few things, it has references to use in event listeners (I'll go more into them in the capturing events section), applies classes from the modules as they're required, triggers the showModal() method when open is set and draws the html (jsx really) to the screen. That's it.

Adding animations

If our modal just appeared when summoned and disappeared when dismissed that would be ok but it would lack the feeling of polish. In my example you'll have noticed there is a slight fade in and move up effect. Let's look and how we did it.

We have two keyframe animations one called show and one called hide. They simply have a start position and an end position.

@keyframes show{  from {    opacity: 0;    transform: translateY(min(100px, 5vh));  }  to {    opacity: 1;    transform: translateY(0%);  }}@keyframes hide{  from {    opacity: 1;    transform: translateY(0%);  }  to {    opacity: 0;    transform: translateY(min(100px, 5vh));  }}

To apply animations we're going to have to know if the modal is opening or closing, this is where our setting of classnames comes in. We will always apply the modal class but we will only apply the closing class when the modal is not open.

// work out which classes should be applied to the dialog elementconst dialogClasses = useMemo(() => {  const _arr = [styles["modal"]];  if (!open) _arr.push(styles["modal--closing"]);  return _arr.join(" ");}, [open]);

Because the modal isn't closed when we remove the open attribute we can assume the modal is [open] but has the closing class.
We use this to apply the show animation when the modal is open and hide animation when the modal is open but has the closing class. We also use forwards as our animation direction so when the animation ends we stay on the last frame.

&[open] {  animation: show 250ms cubic-bezier(0.4, 0, 0.2, 1) forwards;  &.modal--closing {    animation: hide 150ms cubic-bezier(0.4, 0, 0.2, 1) forwards;  }}

You may have noticed that with these changes our modal animates away but does not actually close, if you did, well done you're right and very astute. The next section will show you how we can use the animation to truly close the modal.

Capturing events

We have three things to look at here. Two of which are adding events and third is also adding an event but with a little trickery to allow us to close the modal with js, even though there isn't a method for it.

Closing on backdrop click

We can add a click event to the dialog element but there isn't a way to distinguish between clicking on the backdrop and clicking on the modal. The easiest way around this is to put a container inside the modal and have it take up the entire modal. Now when we click inside the modal the target will be the container and when we click outside the modal the target will be the dialog.

// Eventlistener: trigger onclose when click outsideconst onClick = useCallback(  ({ target }) => {    const { current: el } = modalRef;    if (target === el && !locked) onClose();  },  [locked, onClose]);

Animating away on Escape

By default pressing escape closes the dialog, this is what we want to happen but unfortunately our animation would go with it so instead let's capture the escape press and deal with it ourselves.

// Eventlistener: trigger onclose when cancel detectedconst onCancel = useCallback(  (e) => {    e.preventDefault();    if (!locked) onClose();  },  [locked, onClose]);

Closing with JS

Both of the event listeners we've implemented so far call the onClose function which, as we discussed earlier, doesn't close the modal it just animates it away. In order to turn this animation into a method for closing we're going to need two things, hacky JSX and an event listeners.

Hacky JSX

The dialog element comes with a cool form integration, a button with the value cancel inside a form with the method dialog will close its parent dialog. That's a nice feature, couple that with the ability to hide elements with CSS, take them out of the tab order with tabIndex and the ability to click a button with JS and I think we have our close method.

<form method="dialog" className={styles["modal__close"]}>  <button ref={closeRef} tabIndex={-1} value="cancel">    Cancel  </button></form>

Now we just need to access our closeRef and run the click method on it and our dialog will close.

The Event listener

With the method for closing a dialog under our belt we can turn our attention to calling the method when our close animation is complete, fortunately when an animation completes it fires an event.

We'll need to check that our Modal element isn't open but provided it's not we can click our close button and just like that the dialog is closed.

// Eventlistener: trigger close click on anim endconst onAnimEnd = useCallback(() => {  if (open) return;  const { current: close } = closeRef;  close.click();}, [open]);

Closing thoughts

The web is changing and evolving everyday and it's exciting to keep up with what's going on. Thank you for reading and I hope you enjoyed learning about the dialog element and how to use it in React. If you have any questions, please feel free to ask them in the comments I'll be more than happy to answer them.

As I said at the top I really am proud of my original post about this topic so if you haven't read it and you're interested please head over there and have a look.

If you'd like to connect with me outside of Dev here are my twitter and linkedin come say hi .


Original Link: https://dev.to/link2twenty/react-using-native-dialogs-to-make-a-modal-popup-4b25

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To