James
James

Reputation: 3805

Bootstrap data-toggle event breaks when I change the attributes via jQuery

I have a link which allows a user to set their payment method as their default payment method if it is not already the default payment method. If they click it, I also have a short script that watches for a dom change for the clicked link, and if the href or link text becomes the href or text for the "default" payment method, it will check all of the other links on the page and make sure they are set to say "Make Default" and point to the correct URL's, etc (since one of them would say it is the default payment method).

Anyhow, my issue here is that if I update a link via jQuery, the anchor tag will be correct as far as the HTML goes, but clicking it will not allow the data-toggle to occur anymore.

Here is the initial link setup. The first time you click it, the data-toggle works, so the toggled info changes and it becomes the default. Then, another link like this is clicked, and this one would revert back into this state via my jQuery. At that point, it no longer toggles. I've tried to find a way to trigger the toggle without following through with the event, but if I stop the event, I would stop the toggle. And if I temporarily change the href, then I'm just altering it via jQuery and doing what I'm already doing.

<a class="default-billing default-billing-bank fws-ajax"
    id="<?php echo $method->bank_id; ?>"
    href="/account/billing_bank_default/<?php echo $method->bank_id; ?>"
    data-toggle-href="javascript:void(0)"
    data-toggle-html="Default">Make Default</a>

Here is the jQuery I'm using to reset the default link when another link was clicked and made default.

<script type="text/javascript">
    jQuery(document).ready(function() {
        jQuery('.default-billing').on('click', function() {
            var _this = jQuery(this);

            jQuery('.default-billing').one('DOMSubtreeModified', function() {
                //alert('Changed: ' + $("<div />").append(jQuery(this).clone()).html());
                if (jQuery(this).text() == 'Default' || jQuery(this).prop('href') == 'javascript:void(0)') {
                    //alert('Default');
                    _this.removeClass('default-billing');
                    setTimeout(updateDefaultBillingLinks(), 300);

                    _this.addClass('default-billing');
                }
            });
        });
    });

    function updateDefaultBillingLinks() {
        jQuery('.default-billing').each(function() {
            if (jQuery(this).text() == 'Default') {
                /* I am not using this part, but tried it to see if I could just toggle it manually.  I couldn't.
                jQuery(this).toggle();
                */

                jQuery(this).text('Make Default');
                jQuery(this).data('toggle-href', 'javascript:void(0)');
                jQuery(this).data('toggle-html', 'Default');

                if (jQuery(this).hasClass('.default-billing-card')) {
                    jQuery(this).prop('href', '/account/billing_card_default/' + jQuery(this).prop('id'));
                }

                if (jQuery(this).hasClass('.default-billing-bank')) {
                    jQuery(this).prop('href', '/account/billing_bank_default/' + jQuery(this).prop('id'));
                }
            }
        });
    }
</script>

UPDATE/RESOLVED

Apparently, I did have to use the .data('toggle-[attribute]'), but my other issue was that when I was checking the .hasClass(), I was putting a . in front of the class name. I had to remove that.

Final jQuery:

<script type="text/javascript">
    jQuery(document).ready(function() {
        jQuery('.default-billing').on('click', function() {
            var _this = jQuery(this);

            jQuery('.default-billing').one('DOMSubtreeModified', function() {
                if (jQuery(this).text() == 'Default' || jQuery(this).prop('href') == 'javascript:void(0)') {
                    _this.removeClass('default-billing');

                    jQuery('.default-billing').each(function() {
                        if (jQuery(this).text() == 'Default') {
                            jQuery(this).text('Make Default');
                            jQuery(this).data('toggle-href', 'javascript:void(0)');
                            jQuery(this).data('toggle-html', 'Default');

                            if (jQuery(this).hasClass('default-billing-card')) {
                                jQuery(this).prop('href', '/account/billing_card_default/' + jQuery(this).prop('id'));
                            }

                            if (jQuery(this).hasClass('default-billing-bank')) {
                                jQuery(this).prop('href', '/account/billing_bank_default/' + jQuery(this).prop('id'));
                            }
                        }
                    });

                    _this.addClass('default-billing');
                }
            });
        });
    });
</script>

Upvotes: 1

Views: 849

Answers (2)

Malk
Malk

Reputation: 11983

You can remove the need to monitor the DOM to see if the update was successful by calling the update yourself in javascript. Example:

$('.default-billing').on('click', function(e) {
  var _this = jQuery(this);
  e.preventDefault();
  
  if (_this.hasClass('is-default'))
    return;
  if (_this.closest('table').find('.updating').length > 0)
    return;
  
  _this.addClass('updating');
  
  //$.ajax(this.href)
  // fake Ajax call for test
  __fakeAjax(this.href)
  .then(function(resp) { setDefaultUI(_this); })
  .always( function(){ _this.removeClass('updating') });
  
});

function setDefaultUI($el){
  $el
    .text('Default')
    .addClass('is-default')
    .closest('table')
      .find('.is-default')
      .not($el)
      .text('Make Default')
      .removeClass('is-default')
}
function __fakeAjax(href) {
  console.log(`faking call to ${href}`);
  var d = $.Deferred();
  setTimeout(d.resolve, 1000);
  return d.promise();
};
.is-default {       font-weight:bold; }
.updating::after {  content: '...';   }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<table>
  <caption>Banks
  <tr>
    <td>Bank 1
    <td><a class="default-billing default-billing-bank fws-ajax is-default" href="/account/billing_bank_default/1">Default</a>
  <tr>
    <td>Bank 2
    <td><a class="default-billing default-billing-bank fws-ajax" href="/account/billing_bank_default/2">Make Default</a>
  <tr>
    <td>Bank 3
    <td><a class="default-billing default-billing-bank fws-ajax" href="/account/billing_bank_default/3">Make Default</a>
  <tr>
    <td>Bank 4
    <td><a class="default-billing default-billing-bank fws-ajax" href="/account/billing_bank_default/4">Make Default</a>
</table>


<table>
  <caption>Cards
  <tr>
    <td>Card 1
    <td><a class="default-billing default-billing-card fws-ajax is-default" href="/account/billing_card_default/1">Default</a>
  <tr>
    <td>Card 2
    <td><a class="default-billing default-billing-card fws-ajax" href="/account/billing_card_default/2">Make Default</a>
</table>

Upvotes: 0

Sagi_Avinash_Varma
Sagi_Avinash_Varma

Reputation: 1509

dont use $selector.data method use $selector.attr

bootstrap being a css library depends on html attributes not html5 dataset values

so instead of

jQuery(this).data('toggle-href', 'javascript:void(0)');
jQuery(this).data('toggle-html', 'Default');

use this

jQuery(this).attr('data-toggle-href', 'javascript:void(0)');
jQuery(this).attr('data-toggle-html', 'Default');

Upvotes: 1

Related Questions