There are lots of types of dragging that can happen on websites. While they are all click (or tap), hold down, move, and let go, they are all quite a bit different. For instance:
- Drag files/folders onto the browser window and drop them. The likely use case there is uploading.
- Drag an element on the screen to another (valid) area of the screen. The likely use case is dragging cards from one column to another
- Drag the position of an element. The likely use case is dividers between different areas.
WordPress.com is easily the fastest way to spin up a great-looking WordPress site. Not to mention performant and secure, as your site will be hosted on the great WordPress cloud, and they make those things their concern, not yours. You might think you’d have to give up a lot of control and customizability when you use a fully hosted and managed WordPress service (as opposed to hosting yourself), but that’s actually not true. If you’re on the Business Plan or higher, you can install plugins, SFTP into the server, and even have direct database access like any other host.
[Radio channel adjustment]
Announcer: Today, on CodePen Radio.
Chris Coyier: Hey, everybody! CodePen Radio 335. We're going to get a little technical. One of my favorite kinds of podcasts, of course.
Me and Stephen this week. What's up, Stephen?
Stephen Shaw: Hello, hello.
Chris: Yeah! We thought -- [laughter] I was like, Let's do one called "Life's a Drag" and then we'll talk about all the drag and drop stuff that we just are constantly working on because it's a Web app and it actually comes up a million billion times on CodePen. But that's too sad. You know?
Chris: Kind of trimmed it down to "Coding's a Drag," which is a little more accurate, I think.
Stephen: Yeah. Well, that's what we're talking about: coding.
Chris: It is.
Stephen: Coding drag.
Chris: Yep. Yep. Drag and drop, and what does that mean, exactly? That's worth defining because I think of in a whole bunch of ways. For one thing, the browser literally has drag and drop UIs. I forget what they're even called, but they're unique, though, in that they're isolated to (as far as I know) -- correct me if I'm wrong -- when you pick up a file from not in your browser window and you drag it onto the browser window. That's what those API particularly deal with.
If you tap into that, you get this callback that's like, "A user has dropped a file. It's called tree.jpeg, and it's this big," and all that type of stuff. Right?
Stephen: Yeah. I think that's the main way, like the browser kind of drag events work is just getting something into the browser.
Stephen: Versus actually moving stuff around inside.
Chris: It has nothing to do with that. If you think of, "Oh, drag and drop. That's when you pick up something and move it on a browser window." Nope! It's not that. Not at all. There's no such thing as a browser API for pick up this div and move it over here. We'll get to that.
Stephen: There might be, but I don't know that the support is good or it's just well implemented. I think most everything leans on more native mouse events rather than--
Chris: Yeah, because you have everything you need without a dedicated API, necessarily. We'll get to that in a minute, I think.
If we're cutting this into slices, one of the things that drag and drop is useful -- if we scope it just temporarily to dropping a file on top of the browser window -- on CodePen, that is for you're literally uploading a file. We have this assets feature. You use it for that.
In the projects editor, you drag and drop anything, pretty much - code files, and they upload there and that type of thing.
You know. Why not support it? You know? It's a useful kind of thing. That's that aspect.
Don't we do it multiple ways? You'd think the projects editor might actually use the pretty raw browser APIs. We just use them.
Stephen: Yeah. I don't know that there's any kind of plugin or library being used there. I think it's just some custom event listeners listening for that drop event of a file and then triggering some redux stuff to actually upload the file and show the UI for it.
Chris: Yeah. We kind of wrote it from scratch and it worked well enough. That's cool because then - I don't know - you're not paying for anything and just letting the browser do what it does. It's about as fast as it can possibly be. That's cool, although, it probably doesn't give you all that much.
As far as I know, if you're going to trigger an upload, then you're certainly not going to get an upload progress bar or something. That's on you.
Stephen: Well, we do that have that, actually, implemented, but it's a lot of work to get all of that in place.
Stephen: Then actually handling where the file is going and all that, there's just so much manual work that's happening with all that. That's why, with our assets editor (or just assets feature) when we were rebuilding all of that, we went with a service and a library called Filestack. They handle the uploader area. You drag and drop files onto it. Then it funnels through the S3 bucket--
Stephen: --for us to process. In our case, I think they just have a React component or some kind of component that you slap on the page and it deals with it. I like that because you can't do anything that the browser can't do. Ultimately, it is using native APIs somewhere in it. But they're incentivized to keep that thing super up to date because this stuff A) it's tricky, B) they make it their business in a way that we don't have to then, which is nice because that stuff changes and there are bugs and cross-browser issues - yadda-yadda.
Stephen: Yeah. Think about Nobel.
Chris: Oh, right.
Stephen: That was nice to add support for on uploading with iOS.
Chris: Yeah. I don't even think we bothered. We probably just hid the button on iOS, but now it's like, "Meh. They support it, so might as well." Pretty sweet. Yeah, and then the file upload progress, they handle that because that's just one of the features they have. We don't have to code it on the back-end, too. I think our native implementation, there has to be some server cooperation message bus action that's just like, "Eh. Why bother?"
Chris: Pretty cool. That's useful stuff. The classic bug on CodePen is that you drag a file onto the page and it happens to hit an iFrame, and the iFrame takes the event rather than the parent page. We're forever detecting events, turning off pointer-events on iFrames and crap like that because that is certainly a bug with drag and drop is that the wrong page gets the event.
Stephen: Yeah. Even when it's covered by another element that should eat those drag events or anything like that.
Stephen: For some reason, the iFrame just takes priority there in the browser and it sends those through in a way that's unexpected.
Chris: Yeah, it certainly is. A classic thing that happens in websites, too, is -- I don't know if the world -- I'm certainly not trained in what websites are ready to handle your drag events anyway, so you have to lean on affordances in the UI, like a rectangle with dashed lines around it screams, "You can drop stuff here!" and you can make that happen.
Stephen: Have you ever worked with a form that has the little click to upload kind of button?
Stephen: Just a standard input type equals file.
Stephen: You can actually drag and drop stuff onto that. The browser will handle that.
Stephen: But only if you get it exactly on that button.
Stephen: If you drag it anywhere else, suddenly all this work you've done filling out the form, now you're just staring at the JPEG you wanted to upload.
Chris: Yeah. Wiped out. I'm just noticing this in the past few weeks or so, at least in Chrome. It's the main browser I use.
When you drag and drop onto a window, what it used to do is replace the website you were on with the JPEG - or whatever it is. And it doesn't do that anymore. Now it opens a new tab with that in there.
Chris: Which would kind of stop that form problem. I wonder if they changed that default behavior or if it's on websites to implement some code to do that or what. I'd much prefer that behavior.
[Guitar music starts]
Chris: This episode of CodePen Radio is brought to you by Automattic, the makers of all things WordPress like Jetpack and WooCommerce. And of course, they're the makers of WordPress.com itself, the fastest, most powerful, easiest place to host a WordPress site. It's so quick to get started. You have all the power of any other WordPress hosting. It's truly an incredible product. A fantastic place to spin up a WordPress site. I do it all the time for myself, for clients, for acquaintances, for everything. It is the way to go for WordPress.
Thanks, Automattic, for your sponsorship and high five, WordPress.com.
[Guitar music ends]
Chris: Anyway, that's drag and drop in the context of uploading a file in that you can do it natively and that you can reach for custom solutions, yadda-yadda. Certainly, you would want to abstract it in your codebase a little bit and have something to deal with it. We're always figuring out what the best user experience is for that.
Then there's another type of drag and drop, which we alluded to, which I think of it as classic Trello. There's a card, and you can click on it and hold down the mouse button -- or presumably, it works on mobile too, so a touch event where you don't lift up your finger -- and drag it somewhere else on the page and let it go and it does something.
In Trello land, you could move it up or down on the same stack, or you could move it across columns elsewhere. Really kind of enabled the world of Kanbanning, which (thank God) because Kanban's are awesome.
Chris: What is that? You're saying there might actually be browser, native browser stuff. I don't know if there is.
Stephen: Yeah, so there's the draggable attribute, and then there are actual drag events and drag-in, and all that. You can set actual draggable areas.
Stephen: I haven't messed around too much with the native APIs for it, but we do have some of that integrated with our project editor.
Chris: I see.
Stephen: We've got some drag and drop events that we're listening to, just custom listeners, and then the profile organize showcase page is doing that in the same way. We've got some drop zones and we've got some draggable items. You move them around.
Chris: Nice. We use no lib for it?
Stephen: As far as I can tell. It's pretty ancient code, but it's surprisingly--
Chris: Yeah. I see what you mean, though. You can use -- you can make a draggable attribute and stuff. I see that it's very -- that's the point, though, right? It's pretty fricken' limited.
Chris: Surely on purpose, right? You can get too opinionated with this.
I think there's good reason why not everybody uses it for everything. There's this aspect, when I think of Trello, that's like, imagine two different possibilities. One of them is you click, hold down, and drag to move it away. You might want to literally clone that element right at that moment so the old one just stays where it is and occupies the same amount of pixel space.
Chris: Then while you're dragging it, let's say you just let go somewhere that's invalid, hit the escape key, or something. You want that to just poof away. Well, does the native browser handle that? Maybe it does. But that's some heavy lifting, probably, to get it where it goes.
Or maybe you want to not just clone it, but just leave a placeholder there that's the exact same size. Then style the placeholder. Yeah, there are all these little UX things that you might want out of a library that you'd have to kind of write for yourself. It's no surprise to me, is where I'm going, that there are all kinds of libraries that help with this stuff and provide their own kind of API sugar over draggability.
Stephen: Yeah. That was a big research process when we started implementing reordering for collections. We knew we needed this kind of functionality. You want to be able to drag an item around and kind of organize things.
I was kind of digging through a bunch of different libraries, and we're using React, so it needs to play nicely with that. That's a whole other set of problems, like, how is it implemented? Is it hooks? Are there components? What's the API for all of this?
The one we ended up settling on was react-beautiful-dnd (short for drag and drop, there). That's made by Atlassian. Is that how you say it? I always read it differently.
Chris: Oh, you nailed it.
Stephen: Okay. [Laughter] They are pretty invested in a drag and drop solution for their stuff, so it's well supported. It has kind of a robust feature set and is light-ish weight.
Most of these libraries are all clocking in pretty large.
Stephen: And this one wasn't too bad.
Chris: No exception? Oh, really? It's decently small? I wouldn't be--
It just seems like one of those things that just feels beefy without even--
Stephen: Yeah. It's unfortunate. You think about it just as the feature set of it, like you click an element and you drag it around. Yeah, that's straightforward, I guess. But there are so many little intricacies of it, like just setting up the drop zones and triggering events and the placeholder stuff is really complicated, like trying to duplicate an element and all that.
Chris: Yeah. It's no joke.
Stephen: Yeah. Animating, moving things around, like when you drag it into place, triggering those kinds of transitions. Yeah, there's a lot to it.
Chris: There is. So far, it's been good to us. Even just the API alone makes me fond of it, in a way, where you're like, "Oh, I want something to be draggable." Well, then I'll wrap it in a draggable thing. It's very declarative. "I want this thing to be draggable," and draggables exist within--
I don't think they have to, but there are droppable zones, too. Meaning this area of the page is a place where you can put something. It's very -- I don't know. There's a bunch of callbacks that's like, "Oh, this draggable object got dropped in this droppable zone. Here's information about it so that you can do what you need to do to make stuff happen."
Stephen: Yeah, it's very flexible.
Chris: You know it has been -- you know it's not like there hasn't been rough edges and stuff. I don't know how much of that is -- when I've dealt with it, it's been like, "Is this me that's just a dummy, and I need to spend more time grokking it, or is it actually a rough edge of the software?" It's probably a little of both.
Stephen: Yeah. It's robust enough. It's kind of difficult to just drop into and figure it out. Digging through the docs even is a bit of a task.
Stephen: But it's flexible and it works pretty well for us. We're experimenting more with it for different organizing needs.
Chris: Yeah. Yep.
Stephen: So, that's a good one to try out.
Chris: That's that concept of, "I'm going to pick up something and I'm going to drag it around the page, and you're going to be able to see it happen as I'm dragging it around, and I can drop it and something is probably going to happen when I drop it - or not." You know? The goal is that something does happen, that you're doing something useful.
Like Shaw said, we have reorderable collections. That happens in list view, so you can imagine row, row, row, row, row, row, row, and you can only resize them one direction - vertically. You grab the little thing, the little handle. Even that's an API concern.
Stephen: [Laughter] Yeah.
Chris: Where can you drag it from? Is it the whole row or is it just a part of the row? That's important stuff because if you get it wrong, you can't select it.
Stephen: You don't want it to eat up all the click events or anything.
Stephen: If they're just clicking a link, you don't want to trigger a drag event.
Chris: Exactly. That stuff gets tricky to think about. But it can be done. That's why it's got to have a nice API.
Great. Yeah, you click, drag one, and lift it up. Now it sorts it not only visually but then, of course, on our end, we need to fire off a GraphQL mutation that saves it to the database because, the next time you load that page, it better be in the right order and all that. The APIs for that are pretty nice for us. That's that.
But it's not just reordering that. It's as we're working on other projects on CodePen, you can imagine. Man, what if you made everything draggable? It feels like modern software. I think you could lean into it too far, but it does kind of feel good when you're like, "I'm building a UI that's intended to be flexible. Dragging stuff is just going to be a part of it, for sure." There's that.
Then you have to think in your brain, like, "Is this something that I drag and move and drop, or is this something that's a little more femoral, I guess?" That's this third thing that you have here is resizing. It's kind of like you drag and drop. I guess you do the same thing with your mouse, right? You click. You hold down. You move. You let go. But there's no invalid drop. It just always does what you -- it's just there. Whenever you let go, that's what it's going to do. That also has some native stuff.
In CSS, even, you can just have a div. As long as it has, I guess, some kind of overflow value set on it (other than visible), it will give you a little handle, and you can control vertical or horizontal resizing. It's kind of one of those things, like, "Doesn't just CSS have this?" You're like, "Um... [Laughter] Yes, but no." I’m not sure I would rely. You can't even style the handle, really.
Stephen: Yeah. That was kind of the biggest limitation we were running into with it. If you want a big bar that you can click and drag to make an area resizable, the CSS just doesn't have the properties for that. It can't target the resizer handle at all.
Chris: There are not even any tricks. There's not even a secret WebKit way to do it - or some crap - that we couldn't use anyway. But you can't even do that. There's just no way.
Stephen: I faked it for a little while. When we did the details revamp and the pop-out preview and all of that, I made the iFrame preview in that resizable just using CSS because we were relying on another library that wasn't react-beautiful-dnd, and it wasn't really used anywhere else.
Stephen: It was adding all this weight to the page that didn't need to be there. So, I just stripped that out and replaced it with resize both and did a little pseudo-element that passes through pointer-events none.
Stephen: Over that area to make it a lot more obvious that that was a little draggable spot.
Chris: You can fake the look a little bit.
Chris: By positioning something there, but you can't change the size of it.
Stephen: No. Yeah.
Chris: That's the killer.
Stephen: And so, we were running into some issues with that. It was a little buggy with mouse events and things like that. Whenever you would be in the pop-out preview, it would resize, and your cursor would be off of the main little modal area. You let go; it was triggering a click event on the background of the modal and making the modal close. [Laughter]
Chris: Oh, God.
Stephen: There are a lot of little oddities with that. When it came time for us to redo the embed modal that we did recently where we cleaned all that up and added a little resizable iFrame area that helps you set the height that you want your embed to be.
Chris: Again, it's a bar. You click it. You can drag it up and down in one direction, which should be kind of a settable thing. Yeah. You wanted some control, right? It's time to write some code.
Stephen: Right. For that, it seemed silly to get a whole library for it. Just tracking some click events and some movement and mapping that to some call to actions and everything. I put together a little component in our library just called Dragger.
Stephen: It doesn't have any inherent styles. It doesn't have anything beyond just tracking on mouse down and then mouse move kind of events.
Chris: Right. You figure out where the click happened first and then you're going to get a stream of callbacks when the mouse moves. Right? You can be like, "Oh, it's moved negative ten pixels vertically." You can then react to that, right? It's really raw.
Stephen: Right, and that's exactly what we want out of it because now this allows us to use it for resizing or use it for all kinds of other things where we're just trying to track how much the mouse has moved. Like some kind of slider, we could use. We could use this for all those kinds of things.
In this case, it's just really one small DOM element that you can style however you want, if you want it to be a bar or a little handle in the corner or whatever.
Stephen: Then you click it, and that mouse down event actually triggers an addition to the DOM where it creates this overlay element that's over the entire page so that your mouse move events don't get captured or lost by anything else like the iFrame issue we were having.
Chris: Yeah, that's great. The only thing, there's this huge overlay over the whole page and then just the dragger itself is above that?
Stephen: Well, so the dragger itself is actually now under that.
Stephen: But that overlay has mouse move events attached to it.
Chris: I see.
Stephen: Then mouse up or mouse leave, like all those, and the touch-based ones as well to just be comprehensive. Whenever the mouse is let up or moves off the window, then that element is removed.
Stephen: It calls the end call to action.
Chris: If you want to do things visually then, it's on you to digest the callback and change the view underneath.
Stephen: Right. For instance, in the embed modal, whenever you're clicking and dragging it, we have this little height indicator that shows how tall it is and the number of pixels. We also dim the iFrame so it's a little more obvious what's happening. It's a nice little interaction, but yeah. It's just like a little tiny micro-component that gives you these interactions that you can then use for resizing or any kind of mouse drag interaction.
Chris: Pretty great. It just works tremendously well, right? It's not--
The irony is, to me it seems like, "How can there not--? How can react-beautiful-dnd, in its epic feature set of just dozens and dozens of features that it supports, what it doesn't support is just dragging something?" [Laughter] It's just not something it can do.
Stephen: Yeah. It's interesting. These are all (at the core) the same event, but they're so different in what they're trying to accomplish that something like react-beautiful-dnd or a file uploader library, they're not even going to mess with something like a resize kind of setup because it's just fundamentally different even though it is, you click your mouse, you move it around, and something interacts.
Chris: I know. It's so, so tightly related that it blows my mind that it doesn't support it. But at the same time, it's like, "Yes, but they need to be careful about the API that they expose because it needs to be tight and understandable and not this sprawling mess." I get it.
It's just funny to look in our codebase where there's a react-beautiful-dnd thing right next to another draggable thing that's totally different componentry.
Chris: Pretty weird.
Stephen: Well, and one of the biggest areas, like we do resizing, is the pin editor. You've got the three (HTML, CSS, JS) panels. You drag them around. You've got the preview that's draggable, too.
Chris: Surprisingly complicated, isn't it?
Stephen: Very complicated. Who wrote that back in the day?
Chris: David DeSandro helped.
Chris: You know it's been rewritten a number of times, but once he did it--
And he's famous for lots of things but, on CodePen, that particular little interaction where, once it gets too small, the title of the editor does that 90-degree animation to put its title in there.
Chris: Yeah. It's really nice, and it's kind of like an iconic CodePen thing. We're definitely not getting rid of that any time soon.
Chris: We, in fact, rewrote little parts of it when we did the tabbed look recently in the pen editor. Pretty cool. But think of how it works. You drag one and it makes sense. "Okay, shrink one. Expand the other." Fine.
But there can be a third thing involved. It can be, "Oh, now you've shrunken one as far as it can shrink. Now shrink the thing next to it." That's the stuff that's like, "Boy, this thing needs to be really flexible." It's not just three. It needs to essentially handle an arbitrary number of elements.
Chris: It's like, woo-doggy.
Stephen: Yeah. if you try to look for a third-party library that supports the same kind of resizing setup that we have, it doesn't exist. Everything that's resizing, it's based on some kind of set number of columns or whatever. It just doesn't flow in the nice way that ours does where, if you're resizing one, you want that one to resize. You don't want to reach the end and then now have to resize another column to then go back and resize the original.
Stephen: Then we have other little niceties. If you double click on the drag handle, it full sizes that one.
Chris: Yeah. Animates to that full position, no less, which is nontrivial. Yeah, pretty wild. I think that was written literally before Flexbox and Grid were usable.
Chris: It was set up as an inline-block or floats or something so that they were next to each other. Then every single element was, I think, percentage-based so that as you drag around, it was literally calculating what the percentage for every single child element should be and plopping it on the DOM. Miraculously, it was pretty efficient and just did what it needed to do.
These days, it can be slightly easier in that you can have display grid and then set up the columns and/or rows to just update in one place. But even that, it's not like it's easy because Grid has its own little weirdnesses. You need to make sure that the column can shrink all the way because I think the minimum size for rows and columns is usually min-content.
Chris: So, it doesn't even shrink as far as you want it to.
Stephen: If you just set zero FR or zero or something like that.
Stephen: There are so many grid blowout issues that you have to work around in making sure that things are properly sized and aligned and all that.
Chris: Yeah. Yeah. Setting some min values and max values and using auto properly when you want things to naturally be this. There are loads.
I was just messing with it today because you had some setup for something that was working pretty good. I was like, "Oh, yeah. But I just need to move this panel up into this area now," because that's what we decided we were going to do. I just ruined it.
But then the CSS on the page has influence over how that's going to behave, total, so it's really this two-sided setup where you've got to get both sides of the aspect right.
Stephen: Yeah, and the children and everything else.
Chris: The children can mess with. If you set some child to have some max-width or max-height or something, that's going to win. It doesn't care what the parent has to say about that.
Chris: Anyway. That's fun. There's so much dragging and dropping on websites, dealing with all the file-dropping kind of stuff. There's picking up stuff and dropping it in designated areas. Then there's all the resizing stuff. They are so, so super different. [Laughter]
Chris: No wonder people get frustrated working on websites sometimes. I think you have to be fortunate to be working on a project that you can dedicate that much time to it at all.
Chris: I could see if you're a startup and these aren't your core things. It's kind of a bummer and it's no doubt that people reach for pre-baked solutions for this. I'm sure iOS developers don't deal with this crap.
Stephen: [Laughter] Yeah.
Chris: They just use the little resize machine and it just works.
Stephen: Can you NPM install for Next code?
Chris: [Laughter] Yeah, I don't know. I don't know. I would think even like Flutter probably deals with it pretty efficiently. I haven't actually tried it, but it's like the need to replicate that mobile experience, so they probably have little, special components and stuff.
Fortunately, the React ecosystem is so thick that there tends to be React-based stuff for what we need, and that's the case here, so thanks. [Laughter]
All right. Anything else you can think of related to all this?
Stephen: Uh... no. It's complex, but you know it's an important part of how something feels and functions. If you miss that, it just doesn't have the same effect. You're going to let down your users.
Chris: I totally agree! Yeah. If you're just sitting at your computer right now listening to this, grab your tabs in your browser and just grab one and just move it around. How does it feel? Do you see the tab as you're moving it or do you not? Is it some ghost of the tab?
Do other tabs move away when you get close? Do they move away immediately or do they only move when you get a little close to that environment? How many tabs move?
Just think of all the little interactions that have to do with that. Writing that stuff from scratch is no joke, but it really can affect how quality your software feels.
Stephen: Yeah. Yeah, especially if it's laggy or feels like chunky, like you're resizing and something is just not as responsive as you want it to be. That is a killer.
Chris: Mm-hmm. That's the quintessential definition of jank, I think. Jank is the word we use for that.
All right. Thanks, Shaw.
Chris: Until next time.
[Radio channel adjustment]