- New project, new framework
- Thoughts on News and Copyright
- Changing an IP subnet is no small task
- Why would anyone pay for something that is based on Open Source?
- Creating PDF from code with FOP
- Hints of the future
- Thoughts of servers and maintenance
- Microsoft Submits Windows 7 for Antitrust Review
- Your own Internet Radio on Linux - Updated
- Frustrations with IE and JSON code
I was recently tasked with an interesting project. One of the elements of this project is a calendar like web page where a user must be able to apply some settings to number of days by highlighting the days with the mouse. We've all seen this sort of behavior with spreadsheets, calendar applications, and even word processors. But it is still relatively rare to see anything like it on a web page. So, I had to do some serious digging to figure this one out. To start with I knew there were "mousedown" and "mouseup" events, which I've never had reason to use before. But applying that to a test table, I quickly found that I could easily identify the first table cell clicked, and the table cell where the button was let go. However to do so I had to add unique ID values to each table cell. Unfortunately the app in question will be generating this table dynamically, and I was looking for a way to identify ALL the table cells highlighted without relying on IDs. So I was stuck trying to figure out how to identify the cells between the start and end point Right! Off to Google with ya then! Wouldn't you know it, but there are LOTS of drag and drop tutorials and solutions out there. Too bad most of them deal with moving things around on the page. That's not quite wat I was looking for. Then I happened to turn my head and see one of my books - DHTML Utopia: Modern Web Design Using JavaScript & DOM. This book is one I picked for the text book of the Javascript class I teach. I picked it specificaly because it touched on AJAX and some other advanced topics that could be useful. It turns out that drag and drop is mentioned in passing in one of the sample applications. This example still talked about moving things on the page, but it provided enough of a hint for me to find the answer I was looking for. (The book has proven to a good reference, but not a great text book for beginning Javascript programmers - I'm not using it as the course textbook anymore) One of the topics I've been hearing a lot about lately, especially with regards to the hype around AJAX, is programmatically changing the Document Object Model in an XML manner. I've been able to ignore this for a few years because almost everything they talk about can also be done with the innerHTML property (it's been deprecated or removed from the standards, but it's not disappearing anytime soon). But I was curious if the DOM could be used to figure out the cells between the start and end points. So I slapped together some proof of concept code, and found that the DOM actually makes this task MUCH easier than my first approach. The Document Object ModelIf you've only been using Javascript in a very simplistic or routine manner on your sites, you may not be overly familiar with the document object model. You use it to talk to specific objects on the page.
var myForm = document.forms[0]; //returns a reference to the form
var myParagraph = document.getElementById("myPara"); //returns a reference to the specified paragraph myParagraph.style.color = "red"; //changes the color of the text in the paragraph The samples above all use the DOM, in a manner you have probably seen, if not used yourself. But, the DOM is more than that. The document object model is a tree type structure in memory that represents the objects on your web page. With the advent of XML and XHTML, the model adopted some methods normally only seen when dealing with XML. But we can use these methods to access elements on the web page in a way where we don't always need to use the ID property, like "walking" the tree to get to a specific node, or dealing with sibling nodes. For instance, have you ever needed to know how many table cells were in a specific row? Or how many rows were in a table? Up till now, I would have calculated this with server side code. But I found out today that a table is represented as an object that has a property for a table body object, and that table body has an an array of table rows, each of which has an array of table cells. (phew - that's saying a lot.) So, with that knowledge I can easily apply array principles to determine how many rows are in the table, or how many cells a row has. And even more amazing, each table row object stores a rowIndex property - which tells us WHICH row in the array we are dealing with. And each table cell object stores a cellIndex property that tells us which cell in the array of cells we are dealing with. Armed with that we can react to a click event, and then give the user a message "You clicked on column 4 in row 9" - without us having to do anything special to our table definition. Try this page out to see it in action:
<html>
<head>DOM Sample</head> <body> <table> <script type="text/javascript"> //Determine the row/column number alert("You clicked on column " + theColumn + " and row " + theRow); //assign event handlers to the table cells </body> Click here to see it in action Note that the sample may not work in all browsers due to differences in how events are handled and event handlers are assigned. I believe the sample should be relatively generic though. There's actually lots going on here, and it all shows the power of using the document object model.
This code is pretty simple, and achieves a lot considering we are not explicitly identifying our cells by ID or otherwise indicating the row/column. But there aren't many applications that can use this info as is. With some creative coding and a little additional effort this code becomes extremely useful - as we'll see. The point for now though is that the DOM gives us a way to derive some information about our web page, we would otherwise have to jump through a bunch of hoops to get. The DOM also allows us to modify the web page after it is rendered in some surprising manners. Wouldn't it be nice if we could dynamically add a new text box to a form the moment we need more? Or remove page elements from the DOM completely? These types of coding challenges crop up all the time, and usually result in a revision of how the page works, or the introduction of some server side logic. But no more! Now we can use the DOM and do it client side! So some deep research into DOM will pay off ten fold in the long run. Back to HighlightingThe above code sample shows us how to get the row and column coordinates for a clicked cell. With some extra effort we can now introduce a highlight function. What we need to know for this is the first cell that was clicked, and the last cell where the mouse button was let go. Then we can highlight everything between them. We should also be highlighting cells as the mouse moves over them - so we'll need to make use of the mousedown, mousemove, and mouseup functions. Check out the sample page, and be sure to take a look at the source code for it. This code has the tweaks to make the page cross browser capable, so adds a little more complexity. But a basic description of the routine is that we are using the dragState variable as a flag to indicate if the user is dragging or not. We set the dragState to true when the mouse button goes down (the mousedown event), as well as determining the rowIndex, and cellIndex. Next we track when the mouse moves (mousemove event handled by the mOver function). When the mouse is moving, we check if we are in the dragging state, and if so call the highlight function using the remembered starting cell and the current cell index. We also call a function to cancel processing of the event (this function call can safely be removed). We determine when we are done highlighting when the user lets go of the mouse (the mouseup event), where we change the dragState to false to indicate dragging is done. The important stuff is done in the event handlers as described above, and in the highlight function. In the highlight function we get a reference to the table itself, then from that object to the rows it contains, and use the dragRow variable to determine which row is our target row. Then we do a safety check and make sure we got a valid reference. If so we call the clearHighlight function which simply resets the background colors on all the cells in the row. Next we get a reference to the array of cells in the row. We use a loop to determine which cells should be highlighted. Here you'll see we're using the min/max methods on the Math object to determine the starting and ending point of the loop. This allows us to drag left to right, or right to left without any other special coding. Finally, within the loop we turn on the highlight color for the cell specified by the loop index. The remainder of the Javascript code is simply some generic functions to handle cross browser compatibility, and assigning the event handlers. The code as it stands only allows highlighting of on a single row at a time. But if you look at the highlight function closely you'll see that it wouldn't be too hard to change this to highlight columns instead, or some other combination. This just depends on how you use the start/end points of the drag routine. Ever onwardsWhile this code sample meets the problem posed at the start of this blog, it will form only a very small part of the final product. The drag and drop routines are intended to allow efficient management of a complex page with WAY too much information on it. It's one of those pages that are very *busy* but needs to be. It's also one of those apps that needs to allow quick, easy, and intuitive use of the page because it will be updated constantly by different people, and almost always in a rush situation. Needless to say there is tons of work left to do - but it could not proceed easily until this drag and drop routine was sorted out. If I had tried to tackle this without investigating the DOM, I can easily see the page would not have been as convenient to use and likely would have had to swing to doing most of it's work on the server with page refreshes. But after a few hours of research the DOM, I can see so many ways life just became easier for me. And my this new knowledge will very quickly lead to even more complex applications which would not be possible without really understanding the DOM. I almost feel like I am a trail blazer in this - it's exciting to find a new way to do things. But as is almost always the case, someone has been here before - years ahead of me even. I'm not worried though because I've noticed the the tech industry has those who adopt something early, those who adopt it very late, and those in between. I know a lot of coders who haven't looked at DOM at this level yet - so I'm not a late adopter. But neither am I an early adopter who has to deal with unproven techniques and technologies. I'd like to think I'm a little ahead of the average - or should I say I'm hoping ... |
|||
