views:

77

answers:

2

[Edit by Author - 10/27/2010 @ 08:49 AM CST ]

HTML

<!-- Contents of div #1 -->
<form id="6hgj3y537y2biacb">
    <label for="product_calendar" class="entry_label">Calendar</label>
    <input type="text" name="product_calendar" class="entry" value="" />
</form>
<form id="pyc2w1fs47mbojez">
    <label for="product_calendar" class="entry_label">Calendar</label>
    <input type="text" name="product_calendar" class="entry" value="" />
</form>
<form id="kcmyeng53wvv29pa">
    <label for="product_calendar" class="entry_label">Calendar</label>
    <input type="text" name="product_calendar" class="entry" value="" />
</form>

<!-- Contents of div #2 -->
<div id="calendar_addRemove"> <!-- CSS >> display: none; -->
    <div id="calendar_add">
        <label for="calendar_add" class="calendar_addLabel">Add Occurrence</label>
        <input type="text" name="calendar_add" class="calendar_addInput" value=""/>
    </div>
    <div id="calendar_remove">
        <label for="calendar_remove" class="calendar_removeLabel">Remove Occurrence</label>
        <input type="text" name="calendar_remove" class="calendar_removeInput" value=""/>
    </div>
</div>

Javascript

// Complete behavioral script
$(function() {
    $('input[name=product_calendar]').css({ 'color': '#5fd27d', 'cursor': 'pointer' }).attr({ 'readonly': 'readonly' }); // Additional formatting for specified fields
    $('input[name=product_calendar]').focus(function() { // Focus on any 'input[name=product_calendar]' executes function
        var product_calendar = $(this); // Explicit declaration
        var attr_val = $(product_calendar).attr('value');
        $('#calendar_addRemove input').attr({ 'value': '' }); // Clear input fields
        $('#calendar_addRemove').fadeIn(500, function() { // Display input fields
            $('input[name=calendar_add]').blur(function() { // After value entered, action occurs on blur
                alert('Blur'); // Added for testing
                var add_val = $('input[name=calendar_add]').attr('value');
                if (add_val != '') {
                    alert('Not Blank'); // Added for testing
                    var newAdd_val = attr_val + ' ' + add_val;
                    $(product_calendar).attr({ 'value': newAdd_val });
                    $('#calendar_addRemove').fadeOut(500);
                    }
                else {
                    alert('Blank'); // Added for testing
                    $('#calendar_addRemove').fadeOut(500);
                    }
                });
            $('input[name=calendar_remove]').blur(function() { // After value entered, action occurs on blur
                alert('Blur'); // Added for testing
                var remove_val = $(this).attr('value');
                if (remove_val != '') {
                    alert('Not Blank'); // Added for testing
                    if (attr_val.indexOf(remove_val) != -1) {
                        alert('Eval True'); // Added for testing
                        var newRemove_val = attr_val.replace(remove_val, '');
                        $(product_calendar).attr({ 'value': newRemove_val });
                        $('#calendar_addRemove').fadeOut(500);
                        }
                    else {
                        alert('Eval False'); // Added for testing
                        $('#calendar_remove').append('<p class="error">Occurrence Not Found</p>');
                        $('.error').fadeOut(1500, function() { $(this).remove(); });
                        }
                    }
                else {
                    alert('Blank'); // Added for testing
                    $('#calendar_addRemove').fadeOut(500);
                    }
                });
            });
        });
    });

Now, I've added a few alerts to see the order this script is performing in. When I enter '1234' into input[name=calendar_add] and blur, the alerts come up as expected. Then, when I proceed and enter '1234' into input[name=calendar_remove] and blur, this script throws up alerts in the following order: Blur, Not Blank, Eval False, Blur, Not Blank, Eval True - If I repeat this process, the occurrence of my alerts double every time (both add and remove), however keeping the same order (as if in sets).

I think the issue is multiple value re-declaration of the variable 'attr_val' in the DOM, but I'm not quite sure how to revise my script to alleviate this issue.

+4  A: 

It doesn't. That is not possible.

So, there are some possible reasons that it might seem so:

  • The code that actually runs doesn't look like that. It might be an older version that is cached, or you are looking in the wrong file.

  • The code runs more than once, that way both execution branches may run. (Although I can't really see any possibility for that here.)

  • You are misinterpreting the result, and whatever you see that leads to the conclusion that both branches have to be executed, is in fact caused by some other code.

You could use a debugger to set breakpoints in the code. Set one breakpoint before the condition, and one in each branch. Then you will see if the code runs twice, once or not at all.

Edit:

The alerts that you added to the code shows that the event is actually called twice, and the first time the values are not what you think that they are.

Add some code to try to find out where the event is invoked from. Catch the event object by adding it to the function signature: .blur(function(e) {. Then you can use e.currentTarget to get the element that triggered the event, and display some attributes from it (like it's id) to identify it.

Edit 2:

I am curios about this line:

$(product_calendar).attr({ value: newRemove_val });

Do you create the variable product_calendar somewhere, or did you mean:

$('input[name=product_calendar}').attr({ value: newRemove_val });

Edit 3:

Seeing the complete code, the cause of the double execution is clear. You are adding even handlers inside an event handler, which means that another handler is added every time.

The reason for attr_val not working properly is because it's created as a local variable in one function, and then unsed in another function.

Add the blur handlers from start instead, and they occur only once. Declare the variable outside the function.

Some notes:

  • You can use the val function instead of accessing the value attribute using the attr function.
  • When you assign $(this) to product_calendar, it's a jQuery object. You don't have to use $(product_calendar).
  • The removal doesn't match complete values, so you can add 12 and 2, then remove 2 and you get 1 and 2 left.

(this is a dummy text, because you can't have a code block following a list...)

// Complete behavioral script
$(function() {

  // declare variables in outer scope
  var attr_val;
  var product_calendar;

  $('input[name=product_calendar]')
    .css({ 'color': '#5fd27d', 'cursor': 'pointer' })
    .attr('readonly', 'readonly') // Additional formatting for specified fields
    .focus(function() { // Focus on any 'input[name=product_calendar]' executes function
      product_calendar = $(this); // Explicit declaration
      attr_val = product_calendar.val();
      $('#calendar_addRemove input').val(''); // Clear input fields
      $('#calendar_addRemove').fadeIn(500); // Display input fields
    });

  $('input[name=calendar_add]').blur(function() { // After value entered, action occurs on blur
    var add_val = $(this).val();
    if (add_val != '') {
      product_calendar.val(attr_val + ' ' + add_val);
    }
    $('#calendar_addRemove').fadeOut(500);
  });

  $('input[name=calendar_remove]').blur(function() { // After value entered, action occurs on blur
    var remove_val = $(this).val();
    if (remove_val != '') {
      if (attr_val.indexOf(remove_val) != -1) {
        product_calendar.val(attr_val.replace(remove_val, ''));
        $('#calendar_addRemove').fadeOut(500);
      } else {
        $('#calendar_remove').append('<p class="error">Occurrence Not Found</p>');
        $('.error').fadeOut(1500, function() { $(this).remove(); });
      }
    } else {
      $('#calendar_addRemove').fadeOut(500);
    }
  });

});
Guffa
The code *might* execute more than once if the DOM has more than one input tag with the name "calendar_remove". (See comment above.)
Dan Breslau
@Dan Breslau: The event might be bound to several elements, but only one element at a time will lose focus.
Guffa
I think the issue is multiple re-declaration of the variable 'attr_val' in the DOM. How do I make sure this variable only has one value at any given time? Is there a procedure for resetting a variable and then declaring it again?
Anthony Narvaez
Good catch @Guffa, but I created that variable earlier in the script this segment is part of. "var attr_val = $('input[name=product_calendar]').attr('value');" actually reads "var attr_val = $(product_calendar).attr('value');" in the script as a whole. I changed it when I posted it here for clarification.
Anthony Narvaez
@Anthony Narvaez: Make sure that you only declare the `attr_val` once in the code, and in the outermost scope where it's used, and before it's used the first time. Then it should only exist once.
Guffa
I've edited my question to show all affected elements and the complete script. Give it a re-read and let me know what you think.
Anthony Narvaez
@Anthony Narvaez: Right, the code also executes more than once if you add the same event handler more than once. See my edit above.
Guffa
+1  A: 

OK, I think I understand the issue now.

Every time you do a focus on the product_calendar elements, you do a fadeIn on the #calendar_addRemove element. Every time you do that fadeIn, you use its callback to bind new blur handlers to the calendar_add and calendar_remove elements. That means that over time, those elements will have multiple blur handlers (all executing the same logic.) That can't be what you want.

In the script below, I've pulled out the nested handlers so that they're only bound once to each element. Note that:

  • product_calendar is declared (as null) at the top of the anonymous function, and then updated by the focus handler on the product_calendar element. I think this results in correct behavior.

  • attr_val is declared and assigned locally in both of the blur handlers. Again, I think this results in the correct behavior: If you were to declare it outside of the blur handlers (as product_calendar is declared), then you might accidentally use old values when you access it.

I'm still not sure exactly how this code is supposed to function, but this script performs in a way that I'd consider "reasonable".

(By the way, production code should probably allow for whitespace at the beginning and end of the input strings.)

    $(function() {
    var product_calendar = null;

    $('input[name=product_calendar]').css({ 'color': '#5fd27d', 'cursor': 'pointer' }).attr({ 'readonly': 'readonly' }); // Additional formatting for specified fields

    $('input[name=calendar_add]').blur(function() { // After value entered, action occurs on blur
        alert('Blur'); // Added for testing
        var add_val = $('input[name=calendar_add]').attr('value');
        if (add_val != '') {
            alert('Not Blank'); // Added for testing
            var attr_val = $(product_calendar).attr('value');
            var newAdd_val = attr_val + ' ' + add_val;
            $(product_calendar).attr({ 'value': newAdd_val });
            $('#calendar_addRemove').fadeOut(500);
        }
        else {
            alert('Blank'); // Added for testing
            $('#calendar_addRemove').fadeOut(500);
        }
    });

    $('input[name=calendar_remove]').blur(function() { // After value entered, action occurs on blur
        alert('Blur'); // Added for testing
        var remove_val = $(this).attr('value');
        if (remove_val != '') {
            alert('Not Blank'); // Added for testing
            var attr_val = $(product_calendar).attr('value');
            if (attr_val.indexOf(remove_val) != -1) {
                alert('Eval True'); // Added for testing
                var newRemove_val = attr_val.replace(remove_val, '');
                $(product_calendar).attr({ 'value': newRemove_val });
                $('#calendar_addRemove').fadeOut(500);
            }
            else {
                alert('Eval False'); // Added for testing
                $('#calendar_remove').after('<p class="error">Occurrence Not Found</p>');
                $('.error').fadeOut(1500, function() { $(this).remove(); });
            }
        }
        else {
            alert('Blank'); // Added for testing
            $('#calendar_addRemove').fadeOut(500);
        }
    });

    $('input[name=product_calendar]').focus(function() { // Focus on any 'input[name=product_calendar]' executes function
        product_calendar = $(this);
        $('#calendar_addRemove input').attr({ 'value': '' }); // Clear input fields
        $('#calendar_addRemove').fadeIn(500);
        });
    });
Dan Breslau
I've edited my question to show all affected elements and the complete script. Give it a re-read and let me know what you think.
Anthony Narvaez
I think I see what the issue is; see my revised answer.
Dan Breslau