David Dashifen Kees
2007-Mar-12 22:06 UTC
each() and parameters and unobtrusive javascript
Greetings all, So, I''ve a site where in a list from the database is updated when a new record is added to the database via a form. That part works great. Then, the list that''s generated includes three icons each of which are "buttons" in that when clicked they do something. In the past I''ve either wrapped the image in an anchor with an href of "#" and an onclick action. More recently, I''ve used a form with an input of type "image" to which I can apply an onclick action but that often sends the x/y coordinates in the image input field which has caused some confusion for users in the past. So, what I''ve done this time is the following: 1. Generate the table on the server side along with some javascript utilizing the $$ function and Event.observe to add click events to the images that need them. 2. That server side script is called via Ajax.Updater with the evalScripts flag true so that the javascript runs and sets the event observers as necessary. The downsides to these are that (a) I had to add a specific class to the elements containing the images so that the $$ function could find them, (b) I had to use the name attribute of the image to store database identifiers where are used as parameters in functions called by the click events and (c) I''m not a fan of the inline functions() that are added using the each method. So, that''s enough vagueness, I think, here''s some PHP code: <? foreach($sites as $site) { list($id, $url, $name) = $site; ?> <tr> <td><?=$url?></td> <td><?=$name?></td> <td class="database"><img name="<?=$id?>" src="database.png"></td> <td class="delete"><img name="<?=$id?>" src="delete.png"></td> </tr> <? } ?> That code generates the list of images, this javascript manipulates them: <script type="text/javascript"> var delete_imgs = $$("td.delete img"); delete_imgs.each(function(element) { Event.observe(element, "click", function() { delete_site(element.name) }) }); var database_imgs = $$("td.database img"); database_imgs.each(function(element) { Event.observe(element, "click", function() { window.location.href = "edit_site.php?id="+element.name }) }); </script> It seems to me like there should be a better way to deal with this sort of thing. Yes, I realize that I don''t have to save the arrays in variables and could call $$("td.delete img").each(....) but, functionally, that''s very similar to the above. I may go that direction with the final results, but I''m focusing more on the embedded functions with he each and observe calls. How do you all suggest that these sorts of things be handled? It seems to me that unobtrusive javascript is good, but it''s a pain to actually make it work. Is that the general consensis? Thanks, David Dashifen Kees --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs?hl=en -~----------~----~----~----~------~----~------~--~---
> > It seems to me that unobtrusive javascript is > good, but it''s a pain to actually make it work. Is that the general > consensis?First of all, no that''s not really the consensus. That may be a general truth that speaks to the fact that for some cases it''s not as straight forward as one may think before attempting it, but once learned it''s like riding a bike. Having said that, ask yourself this question: without javascript is it possible, in your application, to even get those HTML fragments into the page? It appears not. It appears you are using a flavor of Ajax where these chunks of HTML you are so worried about applying unobtrusive javascript to would not ever exist without javascript being available to begin with (i.e. to make the Ajax call)... so... why be so worried then about unobtrusive javascript for those HTML bits? Now, to be a bit more helpful, I can just say you''re on the right track. I, however, reserve classes for style, and use ids for element identification and selection. And I would certainly say storing data in a name attribute is not a best practice. If you are eval''ing the returned js... why not set an application variable (or set of them) to transfer state across requests? It''s all a learning process... and it comes in stages. I''d say you''re doing things right so far, just keep trying to improve and keep asking questions. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs?hl=en -~----------~----~----~----~------~----~------~--~---
David Dashifen Kees
2007-Mar-13 01:19 UTC
Re: each() and parameters and unobtrusive javascript
Thanks for your response, but I''m not sure I explained myself well. Firstly, the images are not created with javascript. They''re created on the server side after selecting information from a database and looping over a PHP array. Thus, the images would be available on the client-side without javascript. Thus, there are any number of ways I can use unobtrusive javascript to make sure that the images and "clickable" but also utilize the server-side programming to make sure that non-javascript browsers degrade to less responsive but more functional equivalents. As for using class for styles, id for information, that would be great except that, as far as I''m aware, each id should only be used once per page. Since I need to send the id of the database record from the client to the sever for clicks on two different images, then I''d need two id''s per row. Thus, I used the name field, which can be any thing we want it to and has no other syntactical meaning. What I''m really looking for is a way to avoid all of the inline functions in the javascript. for both of the each() function calls I create an inline function which has one parameter. Then, within that function I end up creating another inline function which takes no parameter but accesses the element.name property. It seems to me that, by doing things this way, the javascript becomes less understandable and the html -- while it remains quite clean and free of javascript -- lacks behavioral information that might otherwise help the stranded and confused future programmer of what''s going on. Perhaps I''m worrying about things a bit to much, after all the site functions exactly the way I want it to, but it seems like a lot of "extra" work just to make the images click-able. I could just add an onclick attribute to the image and call a defined function which would execute the behavior that I desire. I guess, perhaps, what I''m looking for is a solid reason for unobtrusive javascript. So far, no one''s been able to show me that it''s a valuable technique. ''Course, I said the same thing about using CSS positioning for layouts and, by now, I''ve changed that tune. I suspect that, in time, I''ll be convinced, but I don''t think I''m there yet.>--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs?hl=en -~----------~----~----~----~------~----~------~--~---
Hi David, I posted a tutorial on a vaguely related issue over at my blog. It might interest you to have a look: http://tobielangel.com/2007/3/11/a-fistful-of-dollars Let me know if this helped. Regards, Tobie --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs?hl=en -~----------~----~----~----~------~----~------~--~---
On Mar 13, 11:19 am, David Dashifen Kees <dashi...-NT0ononE2K1Wk0Htik3J/w@public.gmane.org> wrote: [...]> As for using class for styles, id for information, that would be great > except that, as far as I''m aware, each id should only be used once per > page. Since I need to send the id of the database record from the > client to the sever for clicks on two different images, then I''d need > two id''s per row. Thus, I used the name field, which can be any thing > we want it to and has no other syntactical meaning.The name attribute is intended to be used to identify elements and should not be used to store values that are more appropriate in the class attribute. "name = cdata [CI] "This attribute names the element so that it may be referred to from style sheets or scripts. Note. This attribute has been included for backwards compatibility. Applications should use the id attribute to identify elements." <URL: http://www.w3.org/TR/html4/struct/objects.html#adef-name-IMG > The HTML class attribute is intended for general values used in processing: "A class name may be shared by several element instances. The class attribute has several roles in HTML: "* As a style sheet selector... "* For general purpose processing by user agents." <URL: http://www.w3.org/TR/html4/struct/global.html#adef-class > You can also create your own custom attributes with whatever values you want. To access them in a cross-browser fashion you need to use getAttribute().> What I''m really looking for is a way to avoid all of the inline > functions in the javascript.Consider putting a single onclick on the table and using event.target/ srcElement to see where it came from. This works very well if you have a table of say 100 records with edit and delete buttons for each record - just one function does it all. If the above is what you are doing, you can get the elements of interest using DOM methods without IDs or names - a click on an edit button tells you which row, you then grab the appropriate cell and provide an edit widget, etc. Any parameters you want stored with the element go in its class attribute or some custom attribute you access with getAttribute. [...]> I could just add an > onclick attribute to the image and call a defined function which would > execute the behavior that I desire.Often whatever is simplest is best, but code bloat can be a real problem with lots of identical inline functions.> I guess, perhaps, what I''m looking for is a solid reason for unobtrusive > javascript. So far, no one''s been able to show me that it''s a valuable > technique. ''Course, I said the same thing about using CSS positioning > for layouts and, by now, I''ve changed that tune. I suspect that, in > time, I''ll be convinced, but I don''t think I''m there yet.Like object oriented programming (or any programming technique), it is not an end in itself. It''s only useful if it provides benefits like a significant reduction in the amount of code sent to the client or in server-side programming (e.g. allocating a class attribute that causes stuff to happen at the client rather than adding in-line code for events). -- Rob --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs?hl=en -~----------~----~----~----~------~----~------~--~---
> You can also create your own custom attributes with whatever values > you want. To access them in a cross-browser fashion you need to use > getAttribute().I''d strongly suggest using Prototype''s readAttribute instead. http://prototypejs.org/api/element/readAttribute Best, Tobie --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs?hl=en -~----------~----~----~----~------~----~------~--~---
Christophe Porteneuve
2007-Mar-13 07:19 UTC
Re: each() and parameters and unobtrusive javascript
I see two things that could help: 1) To use IDs indeed: just add an infix/suffix to your IDs, e.g. <td><img id="img_<?= $id ?>_edit" src="..." /></td> <td><img id="img_<?= $id ?>_delete" src="..." /></td> 2) Why create one click handler per image?! Leverage bubbling, and create a single, non-server-generated (bye-bye this convoluted use of AJAX!) handler at the table level, that uses Event.element(e) to check it''s dealing with one of your images. Something like: Event.observe(''yourTableId'', ''click'', function(e) { var elt = Event.element(e); if (elt.tagName != ''IMG'') return; Event.stop(e); var data = elt.id.split(''_''); window.location.href = data[2] + ''_site.php?id='' + data[1]; }); Notice you don''t need the custom classes anymore... From a more conceptual standpoint, this *doesn''t* degrade gracefully: with no JS, those images are unclickable, and your functionality is lost. These should be <input type="image" /> in a <form>, and you could intercept the form''s submission through JS to rewrite the request without those pesky x/y arguments that you find "confusing to users." -- Christophe Porteneuve a.k.a. TDD "[They] did not know it was impossible, so they did it." --Mark Twain Email: tdd-x+CfDp/qHev2eFz/2MeuCQ@public.gmane.org --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs?hl=en -~----------~----~----~----~------~----~------~--~---
David Dashifen Kees
2007-Mar-13 13:26 UTC
Re: each() and parameters and unobtrusive javascript
I like that, thanks. And the x/y thing isn''t confusing to me, but I''ve actually gotten phone calls about it from people worried that someone is hacking their computer as a result of them. I wish there was a way to turn them off. I''ve resorted to wrapping the image in an anchor tag, too, but then I can''t use post-headers which seems to be the way to go. I''ve never had that problem at other jobs or projects, though, just this one. Oh well. Thanks for the help, all! I''ll take a look at those links you sent me, too, tobie! -- Dash -- Christophe Porteneuve wrote:> I see two things that could help: > > 1) To use IDs indeed: just add an infix/suffix to your IDs, e.g. > > <td><img id="img_<?= $id ?>_edit" src="..." /></td> > <td><img id="img_<?= $id ?>_delete" src="..." /></td> > > 2) Why create one click handler per image?! Leverage bubbling, and > create a single, non-server-generated (bye-bye this convoluted use of > AJAX!) handler at the table level, that uses Event.element(e) to check > it''s dealing with one of your images. Something like: > > Event.observe(''yourTableId'', ''click'', function(e) { > var elt = Event.element(e); > if (elt.tagName != ''IMG'') > return; > Event.stop(e); > var data = elt.id.split(''_''); > window.location.href = data[2] + ''_site.php?id='' + data[1]; > }); > > Notice you don''t need the custom classes anymore... > > >From a more conceptual standpoint, this *doesn''t* degrade gracefully: > with no JS, those images are unclickable, and your functionality is > lost. These should be <input type="image" /> in a <form>, and you could > intercept the form''s submission through JS to rewrite the request > without those pesky x/y arguments that you find "confusing to users." > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs?hl=en -~----------~----~----~----~------~----~------~--~---
All good points. And just to clarify what my position was... I was more or less just trying to urge you to be pragmatic and not get all hung up on this unobtrusive javascript stuff, just for the sake of itself. If what you have works for this given task, don''t get too bent out of shape trying to rework it. On 3/13/07, Christophe Porteneuve <tdd-x+CfDp/qHev2eFz/2MeuCQ@public.gmane.org> wrote:> > > I see two things that could help: > > 1) To use IDs indeed: just add an infix/suffix to your IDs, e.g. > > <td><img id="img_<?= $id ?>_edit" src="..." /></td> > <td><img id="img_<?= $id ?>_delete" src="..." /></td> > > 2) Why create one click handler per image?! Leverage bubbling, and > create a single, non-server-generated (bye-bye this convoluted use of > AJAX!) handler at the table level, that uses Event.element(e) to check > it''s dealing with one of your images. Something like: > > Event.observe(''yourTableId'', ''click'', function(e) { > var elt = Event.element(e); > if (elt.tagName != ''IMG'') > return; > Event.stop(e); > var data = elt.id.split(''_''); > window.location.href = data[2] + ''_site.php?id='' + data[1]; > }); > > Notice you don''t need the custom classes anymore... > > From a more conceptual standpoint, this *doesn''t* degrade gracefully: > with no JS, those images are unclickable, and your functionality is > lost. These should be <input type="image" /> in a <form>, and you could > intercept the form''s submission through JS to rewrite the request > without those pesky x/y arguments that you find "confusing to users." > > -- > Christophe Porteneuve a.k.a. TDD > "[They] did not know it was impossible, so they did it." --Mark Twain > Email: tdd-x+CfDp/qHev2eFz/2MeuCQ@public.gmane.org > > > >-- Ryan Gahl Application Development Consultant Athena Group, Inc. Inquire: 1-920-955-1457 Blog: http://www.someElement.com --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Spinoffs" group. To post to this group, send email to rubyonrails-spinoffs-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-spinoffs-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-spinoffs?hl=en -~----------~----~----~----~------~----~------~--~---