www.webdeveloper.com
Results 1 to 13 of 13

Thread: Best Way of Applying Unobtrusive JavaScript

  1. #1
    Join Date
    Dec 2010
    Posts
    232

    Best Way of Applying Unobtrusive JavaScript

    Basically I'm wondering what do you guys think is the "best" way of applying unobtrusive JavaScript?

    At the moment I have a bunch of functions which I want to apply to specific elements within the page without writing them within the document. Previously I have done this by giving such elements a class named similar to the function I want assigned to it, for example if I want a function called showPicture() to be assigned to the onclick of an element I will give that element a class of showPicture.

    This method seems a little bit cheap and unreliable though, especially considering some elements may need functions to be applied to onclick aswell as onhover etc and doing this in a class name is hard unless I name the class something like "onclick_showPicture onhover_anotherFunction" (seems over the top) and I'm wondering if there are better methods?

  2. #2
    Join Date
    Nov 2010
    Posts
    1,088
    it depends how hardcore you want to be in your unobtrusiveness.

    I should say from the start that I have not 100% swallowed the unobtrusive js kool aid, so my perspective is as biased as anyone else's.

    The moderate approach, and the one that I use most often (because I maintain my own code and don't tend to attach multiple events to single elements at various stages through the script) runs along the lines of:
    Code:
    document.getElementById("blah").onclick=function(){some function stuff}
    and if I want to apply the same event listener to multiple elements I will loop through them, collecting them by tag name, testing them for className, which ends up looking something like this:
    Code:
    var lis=document.getElementById("doclist").getElementsByTagName("li")
    for (var i = 0; i < lis.length; i++) {
    if (lis[i].className=="star"){
    lis[i].onclick=function(){some function stuff}
    	}
    }
    but the seriously unobtrusive players say that's not unobtrusive enough - because if you later assign one of those li's an onclick (or do so from another script accessing the same html) it will override the one that got set in the loop. So they use addEventListener (for everything else) and attachEvent (for IE<9) - using both methods, you can pile event listener upon event listener and they don't wipe previous ones out.

    I'm not that extreme, and I haven't yet run into a situation where I needed to go down that road, but it's an interesting viewpoint and certainly unobtrusiveness is fashionable these days. One of the better websites for "modern" javascript that I've seen is this one - lots of good examples and explanations.

  3. #3
    Join Date
    Dec 2010
    Posts
    232
    Cheers. I'll be having a good look around that site. I used a similar method using getElementsByTagName to get my elements.

    Good point about the event listeners and from what I've seen it definitely does seem to be preferred.

    The main problem I have is when, lets say for example a single element needs to have multiple functions assigned to it's events e.g. test1() and test2() to it's onclick event, test3() when it is hovered and test4() to when it is unhovered.

    Defining all of this inside the elements attributes is the main thing I'm struggling with. Some people have suggested using the rel attribute and using a single class name to indicate elements that need to be assigned javascript which seems a good idea. I was thinking (in regards to the above example) I could do:-

    HTML Code:
    <a href="#" class="hasJs" rel="test1 test2 test3 test4">Click this</a>
    But again this doesn't cater to the fact that I need test1 and 2 applying to onclick, test3 to hover over and test4 to hover out. Doing any more would surely become very complex?

  4. #4
    Join Date
    Aug 2007
    Posts
    3,767
    Well, it depends on the type of functions. For example, I normally wouldn't apply a function to an li, but rather to the containing ul:
    Code:
    var lis=document.getElementById("doclist").getElementsByTagName("li"); // not this but
    var ul = document.getElementById("doclist").onclick = function(e) {
      e = e || window.event;
      var t = e.target || e.srcElement;
      if (/* test t */) {
        ...
      }
    }
    As described here. Other than that, I think it has to be looked at in each individual case.
    Great wit and madness are near allied, and fine a line their bounds divide.

  5. #5
    Join Date
    Dec 2010
    Posts
    232
    Looks good. The function I use at the moment is this:-

    Code:
    function setClicks(){
    
    	ar=document.getElementsByTagName('*');//gets every element
    	
    	for(i=0;i<ar.length;i++){//loops through every element
    		if(ar[i].className.indexOf('j_')!=-1){//checks if element class contains "j_"
    			e=ar[i];//sets the element as the variable "e"		
    			cl=e.className.split(/\s+/);//gets all the classes of the elements and stores them in an array called "cl"
    			for (y=0;y<cl.length;y++){//loops through each class in cl
    			   if(cl[y].indexOf('j_')!=-1){//checks the class contains "j_"
    				  fn=cl[y].substring(2);//creates the function name using the classname minus it's first two characters	 						
    				  e.onclick=(function(fn){
    					return function(evt){
    						evt.stopPropagation;//stops propagation
    						window[fn](this);//assigns the function
    					}})(fn);//sets the new function as the onclick of the element		   
    				}
    			}			
    		}
    	}
    }
    However I now have an element that requires a lot of functionality. When the element is hovered over data which is stored on the said elements "data-info" attribute needs to be displayed, when the element is unhovered this data needs to disappear and when the element is clicked a preview of the item in question needs to be shown. Thats 3 functions for 3 different events on the same element.

    Tricky to do unobtrusively. Here's what my element looked like originally before I started thinking fully unobtrusively to show what I mean better:-

    Code:
    <a href="#" data-info="{"id":7,"type":"23"}" onClick="showPreview(this)" onMouseOver="showDetails(this)" onMouseOut="hidePreview(this)">Item</a>

  6. #6
    Join Date
    Nov 2010
    Posts
    1,088
    it seems fairly simple, but I guess your real-life case is more complex. maybe this is a start...
    Code:
    <body>
    <a href="http://google.com">Some other link we don't care about</a>
    <div id="as">
    <a href="#" data-info="{'id':7,'type':'23'}" >Item 1</a>
    <a href="#" data-info="{'id':8,'type':'25'}" >Item 2</a>
    <a href="#" data-info="{'id':9,'type':'27'}" >Item 3</a>
    </div>
    <div id="deets"></div>
    <div id="prev" ></div>
    
    <script type = "text/javascript">
    var links = document.getElementById("as").getElementsByTagName("a");
    for (i = 0; i < links.length; i++){
    links[i].onmouseover=function(){showPreview(this)}
    links[i].onclick=function(){showDetails(this)}
    links[i].onmouseout=function(){hidePreview()}
    }
    
    function showPreview(el){
    document.getElementById("prev").innerHTML=el.getAttribute('data-info');
    document.getElementById("prev").style.display="block"
    }
    
    function showDetails(el){
    document.getElementById("deets").innerHTML=el.innerHTML
    }
    
    function hidePreview(){
    document.getElementById("prev").style.display="none"
    }
    </script>	
    </body>
    Last edited by xelawho; 05-12-2012 at 09:03 AM.

  7. #7
    Join Date
    Dec 2010
    Posts
    232
    Thanks xelawho. Yep you're right that is pretty simple but what I'm trying to achieve is something like that but which can be applied to many many elements inside a given page or more precisely inside a specific element that I pass to my function, with all sorts of different functions for different elements.

    This is why I tried to assign function names as a classes but this seems like overkill. I may have to experiment with this a little bit:-

    http://davidwalsh.name/event-delegate

  8. #8
    Join Date
    Dec 2010
    Posts
    232
    I've managed to come up with some code and wanted to see if anyone could suggest improvements or point me in the right direction if I'm doing something wrong. I could explain it but I think the code explains itself. The part that I don't like is the part where I'm looping through all the class names and doing if statements to check if a certain class is there and then firing the appropriate function if it is. Based on the amount of functions that I have in my actual code I can imagine this section becoming massive. Is ths the way it's usually done or is there a better method? :-

    Code:
    window.onload=function(){
    	ar=document.getElementById('uiList');
    	if (window.addEventListener){
    		ar.addEventListener("click",function(e){delegate(e);},false);
    		ar.addEventListener("mouseover",function(e){delegate(e);},false);
    		ar.addEventListener("mouseout",function(e){delegate(e);},false);
    	}else{
    		ar.attachEvent("click",function(e){delegate(e);});
    		ar.attachEvent("mouseover",myFunction);		
    		ar.attachEvent("mouseout",myFunction);		
    	}
    };
    
    function delegate(e){
    	if(e.target && e.target.className.indexOf('ui')!=-1){
    		var c = e.target.className.split(" ");
    		if(c){
    			for(i=0;i<c.length;i++){
    				
    				if(e.type=="click"){
    					if(c[i]=="test1"){
    						test1(e.target);
    					}
    
    					if(c[i]=="test2"){
    						test2(e.target);
    					}
    				}
    				
    				if(e.type=="mouseover"){
    					if(c[i]=="test3"){
    						test3(e.target);
    					}
    				}
    
    				if(e.type=="mouseout"){
    					if(c[i]=="test4"){
    						test4(e.target);
    					}
    				}
    								
    			}
    		}
    	}	
    }
    
    <ul id="uiList">
        <li id="1" class="ui test1 test2 test3 test4">Item 1</li>
        <li id="2" class="ui test2 test3 test4">Item 2</li>
        <li id="3" class="ui test1 test2 test3">Item 3</li>
        <li id="4" class="ui test1 test3 test4">Item 4</li>
        <li id="5" class="">Item 5</li>
        <li id="6" class="ui test1 test2 test4">Item 6</li>
    </ul>

  9. #9
    Join Date
    Dec 2010
    Posts
    232
    I'm now thinking rather than having a list of if statements like in my last code i.e. -

    if(classname="something"){fire this function}
    if(classname="something else"){fire another function}

    Then maybe I could simply use the function names as classes as I originally thought? It's just that doesn't account for the fact that some functions are applicable to hover and others to click unless I maybe prefix the classes with letters which represent what type of event they are applicable to for example if I want an element to fire the function test1 when it's hovered I could give it the class h_test1, or if I want it to also fire test2 when it's clicked I could add c_test2.

    I'm a bit lost on this part because I'm not sure on how this is normally done on a large scale as I require it to be.

  10. #10
    Join Date
    Feb 2003
    Location
    Michigan, USA
    Posts
    5,773
    I'll throw my hat in the ring with this solution:

    https://github.com/gburghardt/JsLib/...s/Delegator.js

    Code:
    var controller = {
      init: function() {
        this.delegator = new dom.events.Delegator(this, document.getElementById("foo"));
        this.delegator.addEventType("click");
      },
      save: function(event, element) {
        alert("Save!");
      },
      destroy: function(event, element) {
        alert("Delete!");
      }
    };
    And the HTML:
    Code:
    <div id="foo">
      <button type="button" data-action="cancel">Cancel</button>
      <button type="button" data-action="save">Save</button>
    </div>
    It could easily be changed to use jQuery.

  11. #11
    Join Date
    Feb 2003
    Location
    Michigan, USA
    Posts
    5,773
    Also forgot to add this:
    Code:
    <div id="foo">
      <button type="button" data-action="cancel">Cancel</button>
      <button type="button" data-action="save">Save</button>
    </div>
    <script type="text/javascript">
      controller.init();
    </script>

  12. #12
    Join Date
    Dec 2010
    Posts
    232
    That looks good toicontien. Thanks very much. I'd rather not use someone else's javascript though to be honest because I like to learn as I go.

    One thing that has showed me though is that there is someone else that is thinking along the same lines as me in the sense that the above function you posted works by retrieving the function name from the element itself, in this case from the "data-action" attribute.

    The only problem with this is as I say it doesn't cater to the fact that I need elements which fire several functions based on different events.

    I considered perhaps storing the function names and their corresponding event type in a json object like so:-

    HTML Code:
    data-action='{"test1":"click","test2":"click","test3":"mouseover","test4":"mouseout"}'
    Does that seem over the top? The main problem with it would be that the json object would need to be decoded and looped through every time an event takes place

  13. #13
    Join Date
    Feb 2003
    Location
    Michigan, USA
    Posts
    5,773

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
HTML5 Development Center



Recent Articles