razorbeard
razorbeard

Reputation: 2944

Am I suffering from too much recursion? JQuery - have to click button twice in certain circumstances

I'm making a multiple link locker for a link locking website using their plain-text API (ot: using YQL to accomplish this but haven't gotten that far yet)

It uses localStorage to save the user's API key. But I'm having a problem. Once a key is added and the user hits save, the link adding form is displayed. From here, when one clicks "If this key is incorrect, click here.", nothing happens until the user clicks the link a second time.

The same can be said for the "add" button. Has to be clicked twice. Also, if the add button is clicked, the "If this key is incorrect, click here." link behaves as intended. The main thing is that any clickable element on the page has to be clicked once before either of the clickable elements in question behave as expected.

I thought it might have had to do with default event actions, so I tried using preventDefault() to see if it fixed anything, but to no avail. I'd really like some help from someone more knowledgeable.

I have put the demo online here: API Tools, GitHub repo can be found here: GitHub

Enter any string value into the key form to test. Also, click the "add" button first. Note how the page navigates to "/?"? Would that have anything to do with it? See the code below:

<!DOCTYPE html>

<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Shrink Once API Tools</title>
    <link rel="stylesheet" type="text/css" href="css/bootstrap.css"></link>
    <link rel="stylesheet" type="text/css" href="css/bootstrap-responsive.css"></link>
</head>
<body>
    <div class="container-fluid">
        <div class="row-fluid">
            <div class="span12">
                <h1 class="head-title">Shrink Once API Tools</h1>
            </div>
        </div>
        <div class="row-fluid">
            <div class="span12">
                <div class="flashAlert">

                </div>
            </div>
        </div>
        <div class="row-fluid">
            <div class="span12">
                <div class="mainFormContent">

                </div>
            </div>
        </div>
    </div>

    <!-- Include JavaScript at the very bottom for faster page loads -->
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
    <!--
    Fall back on local JQuery if Google CDN version is unavailable.
    (Since most sites link the Google CDN version, it is more likely
    to already be cached by the user's browser).
    -->
    <script>window.jQuery || document.write('<script src="js/vendor/jquery-1.8.2.min.js"><\/script>')</script>
    <!-- end Fallback -->
    <script src="js/bootstrap.js"></script>
    <script>
        $(function () {
            var apiKeyForm = '<form class="apiKeyForm">'
                           + '<legend>Enter your API Key</legend>'
                           + '<input type="text" id="apiKey" class="span12" placeholder="e.g. ab12c34d5678efgh90123i45678j90k1">'
                           + '<span class="help-block">You can find your access key <a href="https://shrinkonce.com/index.php?menu=usercp#tools" target="blank">here.</a></span>'
                           + '<button id="saveAPIKey" class="btn btn-info btn-large btn-block">Save</button>'
                           + '</form>';

            var apiLinkForm = '<form class="apiLinkForm">'
                            + '<legend>Add a link or two... or more.</legend>'
                            + '<button id="add" class="btn btn-large">Add</button>'
                            + '<button id="remove" class="btn btn-large">Remove</button>'
                            + '<button id="reset" class="btn btn-large">Reset</button>'
                            + '<button class="btn btn-info btn-large">Submit</button>'
                            + '<hr />'
                            + '<div id="linkForm">'
                            + '</div>'
                            + '</form>';

            if (localStorage.length > 0) {
                $('<div class="alert alert-success">Your API token is <b>' + localStorage.getItem('apiKey') + '</b>. If this key is incorrect, click <a href="" class="resetLocalStorage">here</a>.</div>').appendTo('.flashAlert').fadeIn('slow');
                $(apiLinkForm).fadeIn('slow').appendTo('.mainFormContent');
            }
            else {
                $('<div class="alert alert-error">You have not yet entered your API token. Add it below, and it will be persisted in memory.</div>').appendTo('.flashAlert').fadeIn('slow');
                $(apiKeyForm).fadeIn('slow').appendTo('.mainFormContent');
            }

            var i = $('#linkForm input').size() + 1;
            $('#add').click(function (e) {
                e.preventDefault();
                $('<input type="text" id="inputLink' + i + '" class="shrinklink span12" placeholder="http://google.com">').fadeIn('slow').appendTo('#linkForm');
                i++;
                return false;
            });
            $('#remove').click(function (e) {
                e.preventDefault();
                if (i > 1) {
                    $('.shrinklink:last').fadeOut('normal', function () { $(this).remove(); });
                    i--;
                }
                return false;
            });
            $('#reset').click(function (e) {
                e.preventDefault();
                while (i > 1) {
                    $('.shrinklink:last').remove();
                    i--;
                }
                return false;
            });
            $('#saveAPIKey').click(function (e) {
                e.preventDefault();
                if ($('#apiKey').val().length > 0) {
                    localStorage.setItem('apiKey', $('#apiKey').val());
                    $('.alert').replaceWith('<div class="alert alert-success">Your API token is <b>' + localStorage.getItem('apiKey') + '</b>. If this key is incorrect, click <a href="" class="resetLocalStorage">here</a>.</div>');
                    $('.apiKeyForm').replaceWith(apiLinkForm);
                }
                else {
                    $('.alert').replaceWith('<div class="alert">You did not enter your API key! Please enter it below.</div>');
                }
                return false;
            });
            $('.resetLocalStorage').click(function (e) {
                e.preventDefault();
                localStorage.clear();
                location.reload();
                return false;
            });
        });
    </script>
</body>

Upvotes: 1

Views: 3084

Answers (3)

limo2009
limo2009

Reputation: 57

@WTK Thanks a lot! Works also in my situation. I changed my code from:

$('.postAreaDiv').bind('focusin click', function() {
    event.stopPropagation();
    $('.status_label', this).removeClass('visible').addClass('hidden');
    $('.status_textarea', this).focus();
});

to

/* placeholder focus in/out logic */
// focus in
$(document).on('click', '.postAreaDiv', function (e) {
    e.stopPropagation();
    $('.status_label', this).removeClass('visible').addClass('hidden');
    $('.status_textarea', this).focus();
});

It now works perfectly!

Thanks!

Upvotes: 0

WTK
WTK

Reputation: 16971

Problem is, you're binding (by using .click method) event handlers for items that are at that time present in DOM. Those won't work for nodes added later on, which is the case here (you're for example appending message with link to '.mainFormContent').

Easiest fix would be to replace usage of .click with .on method, like this for example:

$(document).on('click', '.resetLocalStorage', function (e) {
    e.preventDefault();
    localStorage.clear();
    location.reload();
    return false;
});

Keep in mind that's just a quick fix. As to architectural approach it's not the best, but that's a whole different question.

Upvotes: 2

Biketire
Biketire

Reputation: 2069

The way I would've added the click listener, would be like this (this also for the readability of your code and eventually re-understanding it all).

if (localStorage.length > 0) {
   var api_key = localStorage.getItem('apiKey');
   var bold_api_key = $("<b>").text(api_key);
   var alert_div = $("<div>").addClass("alert alert-success");
   var pre_span = $("<span>").text("Your Api token is: ");
   var post_span = $("<span>").text(" if this key is incorrect, click ")       
   var wrong_key_link = $("<a>").text("here");

   wrong_key_link.on("click", function(e){
     e.preventDefault();
     localStorage.clear();
     location.reload();
     return false;
   });

   alert_div.append(pre_span, bold_api_key, post_span, wrong_key_link);
   alert_div.appendTo(".flashAlert").fadeIn("slow");
}

I haven't tested this code but I think this might work.

Upvotes: 1

Related Questions