Change Bootstrap 4 tooltip template without refresh

2.6k views Asked by At

I have buttons to bookmark items and the design calls for a tooltip to appear over the bookmark button on hover. Before it's bookmarked, the tooltip should have a yellow background and say "Save". After a user has clicked the button, it should have a green background (and arrow) and read "Saved".

Bootstrap 4 has changed some things, which make CSS targeting more difficult. Namely, the tooltip element doesn't appear in the DOM after the element it's attached to. It appears far at the bottom of the DOM, so .thing--active + .tooltip won't work.

The text change was easy enough. I changed the title attribute on click as well as the data-original-title attribute used by Bootstrap 4. I tried adding a data-template value on click to change the tooltip template used once the item is saved, but it seems that the tooltip style is set on page load. I even tried running .tooltip() on each click to reset the templates each time. No deal.

I thought of using the .on('hidden.bs.tooltip', function () {}) event to pass something new in, but to be honest I'm not sure how I'd start going about using that. Any ideas how to make this change on click?

$( function() {
  var savedTemplate = "<div class='tooltip tooltip--active' role='tooltip'><div class='tooltip-arrow'></div><div class='tooltip-inner'></div></div>";
  $( '.js-bookmark' ).tooltip();
  var bookmarkSwap = function( $el ) {
    if ( $el.hasClass( "is-saved" ) ) {
      $el.removeClass( "is-saved" ).attr( "aria-label", "Bookmark this" ).attr( "title", "Save" )
        // `data-original-title` is set by Bootstrap tooltip.js when
        // title changes. This changes it as well.
        .attr( "data-original-title", "Save" );
      $( '.js-bookmark' ).tooltip();
    } else {
      $el.addClass( "is-saved" ).attr( "aria-label", "Bookmarked" ).attr( "title", "Saved" ).attr( "data-template", savedTemplate )
        // `data-original-title` is set by Bootstrap tooltip.js when
        // title changes. This changes it as well.
        .attr( "data-original-title", "Saved" );
      $( '.js-bookmark' ).tooltip();
    }
  };
  $( '.js-bookmark' ).click( function( e ) {
    bookmarkSwap( $( this ) );
  } );
} );
.tooltip--active .tooltip-inner { background-color: green !important; }
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.3.7/js/tether.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/js/bootstrap.min.js"></script>

<div class="card">
    <a href="#">
        <img class="card-img-top" src="http://lorempixel.com/100/100/nature/" width="100">
    </a>
    <div class="">
        <h3 class="card-title">
            <a href="#">Vendor Name</a>
        </h3>

        <p class="card-text">
            Boise, ID
        </p>
        <button class="js-bookmark save-vendor" aria-label="Bookmark" data-toggle="tooltip" data-placement="top" title="Save">☑️</button>
    </div>
</div>

1

There are 1 answers

1
Chase Gruber On BEST ANSWER

Ended up targeting the tooltip using the aria-describedby attribute that gets added to the button after you call .tooltip(), then I could select the tooltip using jQuery and do whatever with it. The mouseover listener sets the initial style, because I'm lazy and couldn't figure out a way to do it with CSS.

$( function() {
  var bookmarkSwap = function( $el ) {
    if ( $el.hasClass( "is-saved" ) ) {
      $el.removeClass( "is-saved" )
        .attr( "aria-label", "Bookmark this" )
        .attr( "title", "Save" )
        .attr( "data-original-title", "Save" );
      
      var id = $el.attr("aria-describedby");
      changeTooltip(id,false);
      
    } else {
      $el.addClass( "is-saved" )
        .attr( "aria-label", "Bookmarked" )
        .attr( "title", "Saved" )
        .attr( "data-original-title", "Saved" );
        
      var id = $el.attr("aria-describedby");
      changeTooltip(id,true);
      
    }
  };
  
  var changeTooltip = function(id,saved){
    var bg = saved ? "green" : "red";
    var text = saved ? "Saved" : "Save";
    $("#"+id).find(".tooltip-inner").css("background-color",bg).text(text);
  }
  
  var bookmarks = $(".js-bookmark");
  bookmarks.tooltip();
  bookmarks.click( function( e ) {
    bookmarkSwap( $( this ) );
  } );
  
  bookmarks.mouseover( function (e) {
    var id = $(this).attr("aria-describedby");
    var saved = $(this).hasClass("is-saved");
    changeTooltip(id,saved);
    $(this).unbind("mouseover")
  })
  
  
} );
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.3.7/js/tether.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/js/bootstrap.min.js"></script>

<div class="card">
    <a href="#">
        <img class="card-img-top" src="http://lorempixel.com/100/100/nature/" width="100">
    </a>
    <div class="">
        <h3 class="card-title">
            <a href="#">Vendor Name</a>
        </h3>

        <p class="card-text">
            Boise, ID
        </p>
        <button class="js-bookmark save-vendor" aria-label="Bookmark" data-toggle="tooltip" data-placement="top" title="Save">☑️</button>
    </div>
</div>