0

Problem

The tooltips don't work. I have the following error message in Chrome's JS console.

jquery.min.js?v=3.2.1:2 Uncaught TypeError: $elements.tooltip is not a function at Object.removeAllTooltips (Tooltip.js:71) at Object.init (Tooltip.js:59) at ein_tooltip_init (Tooltip.js:75) at HTMLDocument. (Tooltip.js:84) at j (jquery.min.js?v=3.2.1:2) at k (jquery.min.js?v=3.2.1:2)

My Environment

I've got the following setup:

  • Drupal 8.7.4
  • Drush 8.1.17
  • base theme: Radix 8.x-4.3
  • custom sub-theme
  • Node.js v11.7.0
  • yarn 1.13.0

Theme setup

I have the following theme definition in MYTHEME.info.yml:

name: MYTHEME
description: A custom theme for MYTHEME based on Radix.
screenshot: screenshot.png
# core: 8.x
# version: VERSION
engine: twig
type: theme
base theme: radix

regions:
  …

libraries:
  - radix/style
  - MYTHEME/style
  - MYTHEME/tooltip

libraries-override:
  radix/bootstrap: false

ckeditor_stylesheets:
  - assets/css/MYTHEME.style.css

component-libraries:
  ein:
    paths:
      - ./src/components

entity_print:
 node:
   # Adds ein-legislation css library to the Legislation bundle on the Node entity.
    …

# Information added by Drupal.org packaging script on 2018-10-03
version: '8.x-4.0'
core: '8.x'
project: 'radix'
datestamp: 1538551089

The libraries as defined in MYTHEME.libraries.yml:

style:
  …

tooltip:
  js:
    assets/js/Tooltip.js: {}
  dependencies:
    - core/jquery
    - core/jquery.once
    - core/drupal

sidebar-collapse:
  …

legislation-printable:
  …

other-printable:
  …

I can see in the sources tabs that the bootstrap.bundle.js is being included in the webpack, the jQuery is loading fine. The actual JS responsible for invoking the Bootstrap 4 tooltips - Tooltip.js:

'use strict';
//import $ from 'jquery';
// import 'popper.js';
import 'bootstrap/dist/js/bootstrap.bundle';

(function ($) {

  let tooltip_initialized = false;

  function ein_tooltip_init (context) {
    if ( !tooltip_initialized ) {

      console.log(`jQ in tooltip/Tooltip.js`);
      console.dir($);
      // $.noConflict(true);
      if (typeof Object.assign != 'function') {
        Object.assign = function(target, varArgs) {
          'use strict';
          if (target == null) { // TypeError if undefined or null
            throw new TypeError('Cannot convert undefined or null to object');
          }

          var to = Object(target);
          for (var index = 1; index < arguments.length; index++) {
            var nextSource = arguments[index];

            if (nextSource != null) { // Skip over if undefined or null
              for (var nextKey in nextSource) {
                // Avoid bugs when hasOwnProperty is shadowed
                if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                  to[nextKey] = nextSource[nextKey];
                }
              }
            }
          }
          return to;
        };
      }

      const Tooltip = {
        Options: {
          delay: {
            show: 1000,
            hide: 300
          },
          placement: 'left',
          trigger: 'hover',
        },
        init() {
          // Tooltip options for buttons in the bundlemaker
          Tooltip.OptionsBundlemaker = Object.assign({}, Tooltip.Options, { placement: 'bottom' });

          const $anchorsAll = Tooltip.getAllTooltipAnchors();
          const $anchorsBundlemaker = $anchorsAll.filter((index, elem) => elem.parentNode.className === `mybundles-header` );
          const $anchorsOther = $anchorsAll.filter( (index, elem) => elem.parentNode.className != `mybundles-header` );

          Tooltip.removeAllTooltips($anchorsAll);
          $anchorsOther.tooltip(Tooltip.Options);
          $anchorsBundlemaker.tooltip(Tooltip.OptionsBundlemaker);
          console.info(`Tooltip init ✔︎`);
          tooltip_initialized = true;
        },
        getAllTooltipAnchors() {
          return $('[data-toggle="tooltip"]');
        },
        removeAllTooltips($elements) {
          // Kill all tooltips
          $('.tooltip').remove(); // remove any active tooltips
          $elements.tooltip('dispose'); // hide and destroy any existing tooltips
        }
      };

      Tooltip.init();
      return true;
    }
    return false;
  }

  Drupal.behaviors.einTooltip = {
    attach: function (context, settings) {
      $(document).ready(function() {
        ein_tooltip_init(context);
      });
    }
  }
})(jQuery, Drupal);
//export default Tooltip

My package.json:

{
  "name": "MYTHEME",
  "description": "A theme based on Radix.",
  "private": true,
  "scripts": {
    "postinstall": "find node_modules/ -name '*.info' -type f -delete",
    "dev": "NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch": "NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "hot": "NODE_ENV=development webpack-dev-server --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
    "production": "NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
  },
  "devDependencies": {
    "browser-sync": "^2.26.7",
    "browser-sync-webpack-plugin": "^2.2.2",
    "laravel-mix": "^4.1.4",
    "laravel-mix-bundle-analyzer": "^1.0.2",
    "node-sass": "^4.5.0",
    "resolve-url-loader": "^3.1.0",
    "sass-loader": "^7.3.1",
    "vue-template-compiler": "^2.6.10"
  },
  "dependencies": {
    "bootstrap": "^4.3.1",
    "jquery": "^3.3.1",
    "popper.js": "^1.12.5",
    "rupture-sass": "^0.3.0"
  },
  "browserslist": [
    "> 1%",
    "not ie 10",
    "not op_mini all"
  ]
}

  • Where is .tooltip() supposed to come from? From the bootstrap.bundle you are importing in your Tooltip.js (better use lower case file names)? Better add it as separate library and then set it as dependency of yours. – leymannx Sep 10 at 4:19
  • .tooltip() comes from the bootstrap.bundle together with popper.js. I have tried to import these as separate libraries but it didn't work either. – crs1138 Sep 10 at 6:11
  • Don’t forget Drupal doesn’t (and cant) affect anything once the page is loaded and you’re client side. So if jQuery.tooltip doesn’t exist, it’s because it’s not in any of the JS files loaded into the page, or it is and some other code has deleted it before your code runs. Maybe it’s literally missing from the bundle file? Maybe the bundle file isn’t loaded properly? Maybe you’ve got a version of the bundle file which isn’t integrated with jQuery? Approach your debugging as if it was a static HTML site and you should find it easier – Clive Sep 10 at 6:50
  • So, I've found a solution thanks to this hint: I have to import 'jquery'; before loading the bootstrap dependency within the Tooltip.js file. Otherwise the .tooltip() does not get registered as jQuery plugin. This however means, I'm loading jQuery multiple times – with Drupal and bundled in with the Tooltip.js file. Not ideal. How should I go about this the Drupal way? – crs1138 Sep 11 at 11:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.