Page 1 of 3 123 LastLast
Results 1 to 10 of 26

Thread: efficient way to assign fallback values...

  1. #1
    Join Date
    Apr 2008
    Location
    So.Cal
    Posts
    3,634
    Thanks
    63
    Thanked 516 Times in 502 Posts
    Blog Entries
    5

    Default efficient way to assign fallback values...

    here's the situation: I'm using jQuery to select a particular DOM element. If the element doesn't exist, I want to assign a "fallback" element.

    the first thing I tried was
    Code:
    /* "selector1" et.al. are defined elsewhere */
    var element = $( selector1 ) || $( selector2 );
    however, when jQuery finds no matching selectors, it returns an empty object - so, no matter what, $( selector2 ) is never attempted.

    I figured out this much, and it works exactly as desired:
    Code:
    var element = $( selector1 );
    if( !element.length ){ element = $( selector2 ); }
    
    /* which _would_ be fine, I guess, except there are quite a few fallbacks to check. */
    
    if( !element.length ){ element = $( selector3 ); }
    if( !element.length ){ element = $( selector4 ); }
    // etc.
    I could loop it, too. But am I stuck with this approach? Or is there a better way?

    thanks!

  2. #2
    Join Date
    Apr 2008
    Location
    So.Cal
    Posts
    3,634
    Thanks
    63
    Thanked 516 Times in 502 Posts
    Blog Entries
    5

    Default

    further developments...

    because the implementation of each selector varies (i.e., one is a straight selector, one uses .closest(), and so forth), putting them in a loop has forced me to rely on eval(). I don't like that. I'd still prefer a quicker implementation of what I describe in my OP (the one || other || somethingelse concept), but I would settle for weeding eval() out of the loop implementation.

    Here's the actual code, as it stands now:
    Code:
            var selector = [
               "$('[data-container=' + el.attr('data-ajaxtarget') + ']')"
              ,"$(el.closest('data-container'))"
              ,"$('[data-container]')"
              ,"$('body')"
            ];
            for( i in selector ){
              var datatarget = eval(selector[i]);
              if( datatarget.length ){ break; }
            }
    ## EDIT ##
    hmm... loop is 45ms slower (x1000 iterations) than the procedural script.

    ## EDIT #2 ##
    here's another option:
    Code:
        var datatarget = $('[data-container='+el.attr('data-ajaxtarget')+']');
        if( !datatarget.length ){ 
            datatarget = $(el.closest( '[data-container]' ));
            if( !datatarget.length ){ 
                datatarget = $('[data-container]');
                if( !datatarget.length ){ datatarget = $('body'); }
            }
        }
    seems to be the fastest option, especially when the first assignment is successful.
    Last edited by traq; 09-18-2012 at 10:17 PM.

  3. #3
    Join Date
    Apr 2008
    Location
    So.Cal
    Posts
    3,634
    Thanks
    63
    Thanked 516 Times in 502 Posts
    Blog Entries
    5

    Default

    in case you're wondering what this is for,
    Here's the whole thing.
    ^--- please check it out. Not completely satisfied yet, but it works!

    I'd love feedback! I'm still refining my javascript, so I expect there are things to be done better.

    Documentation is forthcoming. For now, try this (make sure to write some content for the external files):
    HTML Code:
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>autoXHR</title>
        <script src="http://code.jquery.com/jquery-1.8.0.min.js"></script>
        <script src="jquery.AT.autoXHR.js">
            /**
             * @author Adrian Testa-Avila <AT@custom-anything.com>
             * @copyright 2012 Adrian Testa-Avila
             * @license Creative Commons Attribution-ShareAlike 3.0 Unported <http://creativecommons.org/licenses/by-sa/3.0/>
             */
        </script>
        <script>$(document).ready(function(){ $('body').ATautoXHR(); });</script>
    </head>
    <body>
        <a href="content1.html" data-autoxhr data-autoxhrto="showresult" >This link will load &lt;content1.html> into the div below.</a>
        <div data-autoxhrinto="showresult"></div>
        <div data-autoxhrinto>
            <a href="somepage.html" data-autoxhr="content2.html">This link loads &lt;content2.html>, not &lt;somepage.html>.</a>
            When clicked, the response html will replace all the contents of this div.  However, 
            <span data-autoxhr="content2.html" data-autoxhrinto style="border-bottom: 1px dotted gray;">this span will only replace its own contents.</span>
        </div>
    </body>
    </html>
    Last edited by traq; 09-19-2012 at 05:18 AM.

  4. #4
    Join Date
    Mar 2005
    Location
    SE PA USA
    Posts
    30,475
    Thanks
    82
    Thanked 3,449 Times in 3,410 Posts
    Blog Entries
    12

    Default

    If you're looking for efficiency and all of your potential elements will be identified by their id, native javascript would probably be fastest for that part. If not - say your selectors could be any valid jQuery selector, then I'd try this:

    Code:
    var selectors = ['#selector1', '.selector2', 'form :text'], count = -1, $temp, $jqobj;
    while(selectors[++count]){
    	if(($temp = $(selectors[count])).size()){
    		$jqobj = $temp;
    		break;
    	}
    }
    if(!$jqobj){alert('Nothing Found!');}
    else {
    	// use the $jqobj jQuery Object found
    }
    That will find the first of the selectors, in the order they appear in the array, that corresponds to at least one element on the page.

    Of course, I'm not sure if this would be more efficient than what you've already worked out, you'd have to time that with a large enough number of selectors and loops that it would mean something as far as comparing efficiency.

    If you like it, it should be made a function or method rather than remain global code as I have presented it here.

    And, as I say, if you're just looking for elements by their id, I can show you what should be a really efficient manner of finding the first of which of those exists on a page.
    - John
    ________________________

    Show Additional Thanks: International Rescue Committee - Donate or: The Ocean Conservancy - Donate or: PayPal - Donate

  5. #5
    Join Date
    Apr 2008
    Location
    So.Cal
    Posts
    3,634
    Thanks
    63
    Thanked 516 Times in 502 Posts
    Blog Entries
    5

    Default

    thanks, John. I discovered after my first post that it's not *just* a matter of selectors - here's the scenario:

    ---------------------------------------
    I'm attaching an event listener to an [arbitrary] element.

    Inside that object, there may be one or more elements with a data-autoXHR attribute.
    The idea is that this element can be used to trigger an ajax call.

    When the user clicks (or, I want to allow for user-chosen events in the next version) the element, the function connected to the event listener is triggered.
    The function does the following:

    - determines the URL to be loaded.
    this can be defined in the data-autoXHR attribute, the element's href element (if present), or a combination of the two.

    - determines which element the response should be loaded into. (this is the part I was asking about above.)
    a "target" element is defined by a data-autoXHRinto attribute. the function looks for the target element in this order:
    1....the trigger element might define the data-autoXHRinto attribute value of the target element
    2....if not, start at the trigger element and search up the DOM and see if the trigger element has a parent with data-autoXHRinto
    3....if not, start at the element where the event listener is attached and search down the DOM for a child with data-autoXHRinto
    4....if not, use the event listener element itself as the target.

    - attempts to load the requested URL into the target container.
    ---------------------------------------

    Because finding the target element involves DOM traversal (and not *just* finding a selector, as I'd originally planned), I had to use evil() (whoops, I meant eval() ) in the loop, which I didn't like. If you know another method

    Can plain js methods easily (&reliably cross-browser) find arbitrary (non-id) attributes?
    How easy is DOM traversal (#2 was made very simple by jQuery's .closest() method)?

    ---------------------------------------

    As far as efficiency goes, everything I've tried so far was pretty close. I tested the original , procedural method; a for loop; and the nested ifs (which is what I ended up using). After a thousand iterations of the search process (finding the target on the first step, finding it last, not finding it at all), everything was between 470 - 590ms. I don't think that translates to a real advantage for any particular method, unless someone has a frakin' huge, messy DOM...?

    ---------------------------------------

    If you have the time, I'd really appreciate feedback on the plugin itself, to.
    Thanks again, John!

  6. #6
    Join Date
    Mar 2005
    Location
    SE PA USA
    Posts
    30,475
    Thanks
    82
    Thanked 3,449 Times in 3,410 Posts
    Blog Entries
    12

    Default

    With jQuery 1.7+:

    Code:
    $(document).on('click', '*[data-autoXHRinto]', function(e){
    	var autoXHRinto = this.getAttribute('data-autoXHRinto');
    	// here you can parse the data-autoXHRinto with regExp to determine what to do
    	// do that and or return false and/or do an e.preventDefault() on the click
    }
    That will listen for clicks on the document. If the target is or bubbles to an element with a data-autoXHRinto attribute, the function will be performed upon that element. I would suggest using the id (and/or other selector) of the recipient element to find the into element rather than using the data-autoXHRinto attribute in an ambiguous way (though that could still be workable I think), and making the data-autoXHRinto a little more complex. Perhaps a comma delimited string that the function could turn into an array and perhaps then into an object:

    HTML Code:
    <a href="somepage.htm" data-autoXHRinto="href:, content2.htm, id:, someid">Wahtever</a>
    <div id="someid"></div>
    Then something like so (untested):

    Code:
    $(document).on('click', '*[data-autoXHRinto]', function(e){
    	var autoXHRinto = this.getAttribute('data-autoXHRinto').split(/, ?/),
    	autoXHRintoObj = {}, i = autoXHRinto.length - 2;
    	while(autoXHRinto[i]{
    		autoXHRintoObj[autoXHRinto[i].replace(/:$/, '')] = autoXHRinto[i + 1];
    		i -= 2;
    	}
    	if(!autoXHRintoObj.href && this.href){
    		autoXHRintoObj.href = this.href;
    	}
    	if(autoXHRintoObj.href && autoXHRintoObj.id){
    		$('#' + autoXHRintoObj.id).load(autoXHRintoObj.href);
    		e.preventDefault();
    	} else if (autoXHRintoObj.href){
    		$(this).load(autoXHRintoObj.href);
    		e.preventDefault();
    	}
    }
    - John
    ________________________

    Show Additional Thanks: International Rescue Committee - Donate or: The Ocean Conservancy - Donate or: PayPal - Donate

  7. #7
    Join Date
    Apr 2008
    Location
    So.Cal
    Posts
    3,634
    Thanks
    63
    Thanked 516 Times in 502 Posts
    Blog Entries
    5

  8. #8
    Join Date
    Apr 2008
    Location
    So.Cal
    Posts
    3,634
    Thanks
    63
    Thanked 516 Times in 502 Posts
    Blog Entries
    5

    Default

    Rewrote it. I used elements of your idea, but I wanted to keep the trigger (the element you click on) in control of the options, so I allow three values for data-autoXHR:
    1. no value. by default, the script will use the element's href (if present) to define the resource to load.
    2. JSON map of options. If a JSON string is used, the script tries to parse options from it. You can specify the resource to load, the target container, and more.
    3. resource URI. if not a JSON string, the script treats the value as a URI to load.

    Demo page.
    Also, I started a repo for my javascript stuff. Check it out! Thanks!

  9. #9
    Join Date
    Mar 2005
    Location
    SE PA USA
    Posts
    30,475
    Thanks
    82
    Thanked 3,449 Times in 3,410 Posts
    Blog Entries
    12

    Default

    Overall it looks pretty good though I haven't gone over it in full detail. Two things I would draw your attention to:

    1. console.log isn't available in all browsers. Even in some where it is, it's not always. In IE 9 for example, if developer tools hasn't been opened yet for that window, you get an error and the link fires normally. You can open and close developer tools that will work. But if you close IE 9 entirely, open it fresh without developer tools, and run the page, you will see. If you want to use console.log in a production version of the code you have to test for it before using it and refrain from using it when it's not found. Or, you might be able to spoof it at the beginning of your code, something like:

      Code:
      if(typeof console === 'undefined'){
      	console = {log: function(){}};
      }
      But that might cause problems, reserved words, or what if it becomes available later? What I've always done is make up my own function:

      Code:
      function logging(msg){
      	if(typeof console !== 'undefined' && console.log){
      		console.log(msg);
      	}
      }
      And then use that to perform any logging desired. Browsers without console or without console.log will do nothing.

    2. Even when everything is working, there seems to be some problem with nested elements in some browsers:

      Code:
      <a href=somepage.html data-autoxhr=content2.html>This link loads <code>content2.html</code>, not <code>somepage.html</code>.</a>
      I'm not sure if that's because bubbling fails or because <code> is a native block level element and therefore technically invalid within an <a> tag. It looks like that because of this bit of code:

      Code:
      })( $( event.target ) )
      That you're analyzing the target tag rather than the bubbled to tag. These should be different if it's the nested tag that's clicked on. I believe substituting this for event.target will get the desired element regardless of nesting. But only if this still at that point in the code represents the data-autoxhr element that was clicked on. As far as I can see it does, and it definitely does at the beginning of the highlighted function:

      Code:
                  $( this ).on(
                      options.triggerEvent
                      ,'['+options.data_autoXHR+']'
                      ,function( event ){
                          console.log('autoXHR: triggered');
                          event.preventDef . . .
    - John
    ________________________

    Show Additional Thanks: International Rescue Committee - Donate or: The Ocean Conservancy - Donate or: PayPal - Donate

  10. #10
    Join Date
    Apr 2008
    Location
    So.Cal
    Posts
    3,634
    Thanks
    63
    Thanked 516 Times in 502 Posts
    Blog Entries
    5

    Default

    Quote Originally Posted by jscheuer1 View Post
    ...console.log isn't available in all browsers.
    I had wondered... the logging was basically for my own benefit (to help debugging), and I left it there just a an "extra." I could just as easily drop it completely. I'm not sure if the average user would even notice.

    Quote Originally Posted by jscheuer1 View Post
    Even when everything is working, there seems to be some problem with nested elements in some browsers:
    Code:
    <a href=somepage.html data-autoxhr=content2.html>This link loads <code>content2.html</code>, not <code>somepage.html</code>.</a>
    I'm not sure if that's because bubbling fails or because <code> is a native block level element and therefore technically invalid within an <a> tag.
    I noticed that last night, after I posted the demo. I completely overlooked the possibility of the <code> element being a problem. I think I have narrowed it down to another issue (when I try{} to parse the attribute to see if it's JSON), but I haven't made that fix yet. I'll report back on that.

    Quote Originally Posted by jscheuer1 View Post
    That you're analyzing the target tag rather than the bubbled to tag. These should be different if it's the nested tag that's clicked on. I believe substituting this for event.target will get the desired element regardless of nesting. But only if this still at that point in the code represents the data-autoxhr element that was clicked on.
    Yeah... that was intentional, though I see it may be causing some problems. Here's what I'm trying to achieve:

    Basically, there are three elements this script deals with:

    1. the "listener" element - that is, whichever element you call .autoXHR() on: that's where the event listener is attached. In my demo, this is the <body>, but it could be any element (the effects would be limited to that element's children; creating the possibility of multiple .autoXHR()'s, possibly with different configuration settings).

    2. the "target" element - the element which will be the container for the ajax-loaded content (defined by [data-XHRinto]).

    3. the "trigger" element - the element which is clicked (or whatever event you choose) and starts the whole thing off (defined by [data-autoXHR]). This is the event.target element. I wanted all of the ajax-call-specific options to be defined by this element, rather than by the target (which may be targeted by any number of triggers) or the listener (which may contain any number of triggers).

    The [data-XHRinto] attribute (on the "target" element) may either be void, or hold an arbitrary (and not necessarily unique) name for the target.

    The [data-autoXHR] attribute (on the "trigger" element) may have one of three values:
    - JSON string: representing a config object for the trigger (resource, target, data to pass to the server, etc.)
    - non-JSON string: interpreted as the URI of the resource to load
    - void: script falls back to the [href] value to specify the resource to load.

Similar Threads

  1. CSS fallback for Flex Level Dropdown
    By ellenchristine in forum Dynamic Drive scripts help
    Replies: 0
    Last Post: 07-27-2010, 01:49 PM
  2. Is getimagesize efficient??
    By php_techy in forum PHP
    Replies: 3
    Last Post: 07-09-2010, 08:15 PM
  3. Replies: 2
    Last Post: 01-09-2010, 08:14 AM
  4. Most efficient way to use includes
    By tbronson in forum PHP
    Replies: 2
    Last Post: 04-08-2009, 12:35 PM
  5. Making code more efficient?
    By Schmoopy in forum JavaScript
    Replies: 18
    Last Post: 11-05-2008, 03:56 AM

Bookmarks

Posting Permissions

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