Поповер Bootstrap 5 в адаптивном JS DataTable не работает должным образом

Я создаю веб-страницу для хобби, используя Bootstrap 5.3.3 и JS DataTables 2.0.5. На странице у меня есть таблица с данными некоторых игроков. В таблице я сделал так, что при нажатии на некоторые ячейки отображается всплывающее окно с дополнительной информацией. Есть все необходимые модули, и всплывающее окно работает нормально, когда таблица отображается во всю ширину. Однако, если я настрою размер экрана так, чтобы некоторые столбцы свернулись, всплывающее окно для этих столбцов не будет отображаться.

Пример воспроизведения в JS Fiddle:

<head>
  <meta charset = "utf-8">
  <title>Responsive Table Popover</title>
  <meta name = "description" content = "">
  <meta name = "author" content = "">
  <meta name = "viewport" content = "width=device-width,initial-scale=1">
  <link href = "https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel = "stylesheet" integrity = "sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin = "anonymous">
  <link href = "https://cdn.datatables.net/2.0.5/css/dataTables.bootstrap5.min.css" rel = "stylesheet">
  <link href = "https://cdn.datatables.net/responsive/3.0.2/css/responsive.bootstrap5.min.css" rel = "stylesheet">
</head>
<body>
  <div class = "container">
    <table id = "players-data" class = "table table-striped" style = "font-size:13px; width: 100%;">
        <thead style = "white-space: nowrap;">
            <tr>
                <th>Player</th>
                <th>Details 1</th>
                <th>Details 2</th>
            </tr>
        </thead>
        <tbody>
        </tbody>
    </table>
    </div>
    <script src = "https://code.jquery.com/jquery-3.7.1.min.js" integrity = "sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo = " crossorigin = "anonymous"> </script>
    <script charset = "utf8" src = "https://cdn.datatables.net/2.0.5/js/dataTables.min.js"></script>
    <script charset = "utf8" src = "https://cdn.datatables.net/2.0.5/js/dataTables.bootstrap5.min.js"></script>
    <script charset = "utf8" src = "https://cdn.datatables.net/responsive/3.0.2/js/dataTables.responsive.min.js"></script>
    <script charset = "utf8" src = "https://cdn.datatables.net/responsive/3.0.2/js/responsive.bootstrap5.min.js"></script>
    <script src = "https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity = "sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin = "anonymous"></script>
    <script type = "text/javascript">
      let players = [
        {player: 'Best', details_1: 'Cool', details_2: 'Sharp', details_1_extra: 'Extra Cool', details_2_extra: 'Extra Sharp'},
        {player: 'Worst', details_1: 'Hot', details_2: 'Slow', details_1_extra: 'Extra Hot', details_2_extra: 'Extra Slow'}
      ];

      $('#players-data').DataTable({
        data: players,
        columns: [
            {data: 'player', render: DataTable.render.text(), sortable: true},
            {data: 'details_1', sortable: false, class: "text-center", render: function (data, type, row) {
                if (data)
                {
                    return `<a 
                        tabindex = "0" 
                        type = "button" 
                        class = "btn btn-link btn-sm" 
                        data-bs-container = "body" 
                        data-bs-trigger = "focus" 
                        data-bs-toggle = "popover" 
                        data-bs-placement = "bottom" 
                        data-bs-html = "true" 
                        data-bs-custom-class = "custom-popover"
                        data-bs-content = "${row.details_1_extra}">
                        ${data}
                    </a>`;
                } else {
                    return data;
                }
            }},
            {data: 'details_2', sortable: false, class: "text-center", render: function (data, type, row) {
                if (data)
                {
                    return `<a 
                        tabindex = "0" 
                        type = "button" 
                        class = "btn btn-link btn-sm" 
                        data-bs-container = "body" 
                        data-bs-trigger = "focus" 
                        data-bs-toggle = "popover" 
                        data-bs-placement = "bottom" 
                        data-bs-html = "true" 
                        data-bs-custom-class = "custom-popover" 
                        data-bs-content = "${row.details_2_extra}">
                        ${data}
                    </a>`;
                } else {
                    return data;
                }
            }}
        ],
        destroy: true,
        searching: false, 
        paging: true, 
        info: false,
        ordering: true,
        stateSave: true,
        responsive: true,
        drawCallback: function (settings) {
            const popoverTriggerList = document.querySelectorAll('[data-bs-toggle = "popover"]');
            const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl));
        }
    });
    </script>
</body>

Когда таблица имеет полную ширину (показаны все столбцы), всплывающие окна в обоих столбцах работают нормально:

Когда столбец «Подробности 2» свернут из-за размера экрана, всплывающее окно больше не отображается в нем, независимо от того, нажимаете ли вы на текст ячейки:

Чего мне не хватает в моем коде, чтобы всплывающие окна работали несмотря ни на что?

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
257
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы должны сделать элемент таблицы дочерним элементом элемента div, который имеет класс «table-Response», например:

<div class = "table-responsive">
       <table id = "players-data" class = "table table-striped" style = "font-size:13px; width: 100%;">
            <thead style = "white-space: nowrap;">
                <tr>
                    <th>Player</th>
                    <th>Details 1</th>
                    <th>Details 2</th>
                </tr>
            </thead>
            <tbody>
            </tbody>
        </table>
    </div>

все еще не работает, я попробовал это в JS Fiddle

Oleksandr 04.05.2024 14:17

Попробуйте инициализировать всплывающие окна с помощью «$(document).ready(function() { })» перед их использованием.

Marvin Calvo 04.05.2024 15:13

$(document).ready(function() {const popoverTriggerList = document.querySelectorAll('[data-bs-toggle = "popover"]'); const popoverList = [...popoverTriggerList].map(popoverTriggerEl => новый бутстрап .Popover(popoverTriggerEl) });

Marvin Calvo 04.05.2024 15:14

нет, они должны быть инициализированы в drawCallback, поскольку они создаются динамически в столбцах DataTable. Не помогает в любом случае.

Oleksandr 04.05.2024 15:23
Ответ принят как подходящий

Когда размер datatable изменяется и при развертывании свернутых ячеек, datatable фактически добавляет еще одну строку, которая содержит свернутые ячейки, но в элементе ul/li/span, т. е. создает новые элементы DOM, к которым не подключено всплывающее окно или какой-либо другой прослушиватель.

пример вновь добавленных элементов:

<tr data-dt-row = "1" class = "child">
  <td class = "child" colspan = "1">
    <ul data-dtr-index = "1" class = "dtr-details">
      <li class = " text-center" data-dtr-index = "1" data-dt-row = "1" data-dt-column = "1">
        <span class = "dtr-title">Details 1</span> <span class = "dtr-data"><a tabindex = "0" type = "button" class = "btn btn-link btn-sm" data-bs-container = "body" data-bs-trigger = "focus" data-bs-toggle = "popover" data-bs-placement = "bottom" data-bs-html = "true" data-bs-custom-class = "custom-popover" data-bs-content = "Extra Hot">
                        Hot
                    </a></span></li>
      <li class = " text-center" data-dtr-index = "2" data-dt-row = "1" data-dt-column = "2"><span class = "dtr-title">Details 2</span> <span class = "dtr-data"><a tabindex = "0" type = "button" class = "btn btn-link btn-sm" data-bs-container = "body" data-bs-trigger = "focus" data-bs-toggle = "popover" data-bs-placement = "bottom" data-bs-html = "true" data-bs-custom-class = "custom-popover" data-bs-content = "Extra Slow">
                        Slow
                    </a></span></li>
    </ul>
  </td>
</tr>

Итак, что вам нужно сделать, так это при развертывании скрытых ячеек, то есть когда datatable добавляет эти элементы, найти и инициировать всплывающее окно для этих вновь добавленных элементов.

Это можно сделать, добавив отдельный прослушиватель ко всей таблице или, точнее, к сворачивающемуся элементу, который имеет класс .dtr-control, а затем найдите вновь добавленную строку (которая является следующим родственным элементом родительского элемента управления с классом .child) и найдите в ней все недавно добавленные всплывающие окна и инициируйте их:

$('#players-data').on('click', 'td.dtr-control',  function (evt) {

const popovers = $(this).parent().next('.child').find('[data-bs-toggle = "popover"]')
.each((i,el)=>new bootstrap.Popover(el));

});

демо:

<head>
  <meta charset = "utf-8">
  <title>Responsive Table Popover</title>
  <meta name = "description" content = "">
  <meta name = "author" content = "">
  <meta name = "viewport" content = "width=device-width,initial-scale=1">
  <link href = "https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel = "stylesheet" integrity = "sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin = "anonymous">
  <link href = "https://cdn.datatables.net/2.0.5/css/dataTables.bootstrap5.min.css" rel = "stylesheet">
  <link href = "https://cdn.datatables.net/responsive/3.0.2/css/responsive.bootstrap5.min.css" rel = "stylesheet">
</head>

<body>
  <div class = "container">
    <table id = "players-data" class = "table table-striped" style = "font-size:13px; width: 100%;">
      <thead style = "white-space: nowrap;">
        <tr>
          <th>Player</th>
          <th>Details 1</th>
          <th>Details 2</th>
        </tr>
      </thead>
      <tbody>
      </tbody>
    </table>
  </div>
  <script src = "https://code.jquery.com/jquery-3.7.1.min.js" integrity = "sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo = " crossorigin = "anonymous">
  </script>
  <script charset = "utf8" src = "https://cdn.datatables.net/2.0.5/js/dataTables.min.js"></script>
  <script charset = "utf8" src = "https://cdn.datatables.net/2.0.5/js/dataTables.bootstrap5.min.js"></script>
  <script charset = "utf8" src = "https://cdn.datatables.net/responsive/3.0.2/js/dataTables.responsive.min.js"></script>
  <script charset = "utf8" src = "https://cdn.datatables.net/responsive/3.0.2/js/responsive.bootstrap5.min.js"></script>
  <script src = "https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity = "sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin = "anonymous"></script>
  <script type = "text/javascript">
    let players = [{
        player: 'Best',
        details_1: 'Cool',
        details_2: 'Sharp',
        details_1_extra: 'Extra Cool',
        details_2_extra: 'Extra Sharp'
      },
      {
        player: 'Worst',
        details_1: 'Hot',
        details_2: 'Slow',
        details_1_extra: 'Extra Hot',
        details_2_extra: 'Extra Slow'
      }
    ];

    $('#players-data').DataTable({
      data: players,
      columns: [{
          data: 'player',
          render: DataTable.render.text(),
          sortable: true
        },
        {
          data: 'details_1',
          sortable: false,
          class: "text-center",
          render: function(data, type, row) {
            if (data) {
              return `<a 
                        tabindex = "0" 
                        type = "button" 
                        class = "btn btn-link btn-sm" 
                        data-bs-container = "body" 
                        data-bs-trigger = "focus" 
                        data-bs-toggle = "popover" 
                        data-bs-placement = "bottom" 
                        data-bs-html = "true" 
                        data-bs-custom-class = "custom-popover"
                        data-bs-content = "${row.details_1_extra}">
                        ${data}
                    </a>`;
            } else {
              return data;
            }
          }
        },
        {
          data: 'details_2',
          sortable: false,
          class: "text-center",
          render: function(data, type, row) {
            if (data) {
              return `<a 
                        tabindex = "0" 
                        type = "button" 
                        class = "btn btn-link btn-sm" 
                        data-bs-container = "body" 
                        data-bs-trigger = "focus" 
                        data-bs-toggle = "popover" 
                        data-bs-placement = "bottom" 
                        data-bs-html = "true" 
                        data-bs-custom-class = "custom-popover" 
                        data-bs-content = "${row.details_2_extra}">
                        ${data}
                    </a>`;
            } else {
              return data;
            }
          }
        }
      ],
      destroy: true,
      searching: false,
      paging: true,
      info: false,
      ordering: true,
      stateSave: true,
      responsive: true,
      drawCallback: function(settings) {
        const popoverTriggerList = document.querySelectorAll('[data-bs-toggle = "popover"]');
        const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl));
      }
    });


    $('#players-data').on('click', 'td.dtr-control', function(evt) {

      const popovers = $(this).parent().next('.child').find('[data-bs-toggle = "popover"]')
        .each((i, el) => new bootstrap.Popover(el));

    });
  </script>
</body>

Круто, работает, спасибо! Есть ли где-нибудь в документах DataTable это можно увидеть или это личный опыт?

Oleksandr 05.05.2024 11:08

ну, нужно покопаться в коде. вариант реагирования описан здесь: datatables.net/reference/option, но я не уверен, что какой-либо вариант охватывает ваш случай, а также, похоже, явно не упоминается, что он добавляет новые элементы в DOM, что приводит к любые слушатели, которые будут удалены, включая экземпляр popover

traynor 05.05.2024 11:31

Другие вопросы по теме

Похожие вопросы