Sergei_Erjemin
@Sergei_Erjemin
Улыбайся, будь самураем...

Почему погружаемый JavaScript на локальной машине работает, а при загрузке на сервер не хочет?

Имеется страница, на которую через load подгружается блок с таблицей. И есть JavaScript который работает с динамической (на стороне клиента) сортировкой колонок в этой таблице. На локальной машине (с локальным сервером) все работает, но с сервера не хочет. Пробовал несколько способов:

Вариант 1
Решаем в лоб:
<div id="to_load">Сюда подгружаем таблицу рейтинга</div>
<script type="text/javascript">
$('#to_load').load('/stat/rating/profiles_rank/ #for_including1');
</script>

При этом на самой '/stat/rating/profiles_rank/' внутрь блока #for_including1 помещен подгрузка скрипта:
<script src="/static/js/sortable-table.js" type="text/javascript"></script>
или сам скрипт целиком.

На локале работает, на сервере нет.

Вариант 2
Пробуем подгрузку JavaScriot только после завершения load:
<div id="to_load">Сюда подгружаем таблицу рейтинга</div>
<script type="text/javascript">
  $('#to_load').load('/stat/rating/profiles_rank/ #for_including1', function () {
    $.getScript('/static/js/sortable-table.js');
  });
</script>

Из блока #for_including1 убираем скрипт (хотя эти и ничего не меняет).

На локале работает, на сервере нет.

Вариант 3
Возможно, в предыдущем примере load сообщил что все ОК! но имел в виду, что загрузка возможна (url '/stat/rating/profiles_rank/' доступен и блок '#to_load' тоже), но сама загрузка завершена не была. Тогда вот так:
<div id="to_load">Сюда подгружаем таблицу рейтинга</div>&nbsp;
<script type="text/javascript">
  $.when($('#to_load').load('/stat/rating/profiles_rank/ #for_including1')).then(function(){
    $.getScript('/static/js/sortable-table.js');
  })
</script>


На локале работает, на сервере нет.

Размышления 1
В логах локальной машины видно, что реально скрипт '/static/js/sortable-table.js' может загружаться как после получения таблицы '/stat/rating/profiles_rank/', так и до него. На работоспособности это не сказывается.

При этом на сервере JavaScript явно загружается и частично отрабатывает: появляются метки сортировки и происходит инициализация таблицы (построчное <tr>-чтение таблицы, удаление и построчный вывод после первичной сортировки). Но дальше работать не желает.

Извращения:
Если ставить задержки для загрузки JavaScript, например вот так:
<script type="text/javascript">
  $('#to_load').load('/stat/rating/profiles_rank/ #for_including1', function () {
    setTimeout(function(){
      $.getScript('/static/js/sortable-table.js');
    }, 1000);
    // alert('готово');
  });
</script>

Вообще ничего и нигде не работает.

Размышления 2
Возможно дело в скрипте сортировки таблицы. Сам я его не придумывал, а модифицировал готовый. Но получилось так:
/**
 * таблица со встроенной сортировкой
 * /static/js/sortable-table.js
 * Created 18.июл.2017.
 * оригинальный скрипт взят из: https://bootsnipp.com/snippets/4OZQx
 */
(function (document) {
  'use strict';
  var LightTableSorter = (function (Arr) {
    var _th, _cellIndex, _order = '';
    function _text(row) {
      return row.cells.item(_cellIndex).getAttribute("data-sort").toLowerCase().replace(".", "");
    }
    function _sort(a, b) {
      var va = _text(a), vb = _text(b), n = parseInt(va, 10);
      if (n) va = n, vb = parseInt(vb, 10);
      return va > vb ? 1 : va < vb ? -1 : 0;
    }
    function _toggle() {
      var c = _order !== 'asc' ? 'asc' : 'desc';
      _th.className = (_th.className.replace(_order, '') + ' ' + c).trim();
      _order = c;
    }
    function _reset() {
      _th.className = _th.className.replace('asc', '').replace('desc', '');
      _order = '';
    }
    function onClickEvent(e) {
      if (_th && _cellIndex !== e.target.cellIndex) _reset();
      _th = e.target;
      _cellIndex = _th.cellIndex;
      var tbody = _th.offsetParent.getElementsByTagName('tbody')[0], rows = tbody.rows;
      if (rows) {
        rows = Arr.sort.call(Arr.slice.call(rows, 0), _sort);
        if (_order === 'asc') Arr.reverse.call(rows);
        _toggle();
        tbody.innerHtml = '';
        Arr.forEach.call(rows, function (row){tbody.appendChild(row);});
      }
    }
    return {
      init: function () {
        var ths = document.getElementsByTagName('th');
        Arr.forEach.call(ths, function (th) {th.onclick = onClickEvent;});
      }
    };
  })(Array.prototype);
  document.addEventListener('readystatechange', function(){
    if (document.readyState === 'complete') LightTableSorter.init();
  }, false);
})(document);

Понимаю его только отчасти. Но очевидно, что LightTableSorter.init(); происходит. Возможно, что onclick конфликтует с чем-то, но почему он не конфликтует на подгружаемой странице '/stat/rating/profiles_rank/' где все тоже самое, просто загрузка скрипта стационарна, а не через $.getScript?

На всякий случай url странички где формируется таблица и все работает как надо: https://oknardia.ru/stat/rating/profiles_rank/. И url c подгружаемом через load варианте, где работать не желает: https://oknardia.ru/blogpost/17/Nagljadnoe-sravnen....

Собственно, пока идей нет, но проблему хочется решить.

UPD1: Заметил, что иногда в подгружаемом вариант скрипт все таки работает как положено. Примерно в 2-5% случаев. Почему??

UPD2: Оборачивание некоторых функций сортировщика в try{...}cache(e){} немного улучшило ситуацию. В частности:
document.addEventListener('readystatechange', function(){
    // if (document.readyState === 'complete') LightTableSorter.init();
    try{if (document.readyState === 'complete') LightTableSorter.init();}catch(e){}
  }, false);

Но скорее всего дело все равно не в этом. На более развесистых DOM (например: https://oknardia.ru/blog/P0) все равно не работает.
  • Вопрос задан
  • 2068 просмотров
Решения вопроса 2
Вот тут видно, что XHR sortable-table.js загрузился до события load (красная линия), а значит в момент события он выполняется. При этом сам динамический контент (таблица, синий блок profiles_rank/) еще только загружается, а значит его пока нет в DOM.
70556ab2962046829cb932305890a6ae.png

Иногда работает, когда контент появляется раньше, чем срабатывает инициализация библиотеки сортировки. Подгружать скрипт сортировки асинхронно не нужно, он уже в себе ждет когда страница и все ресурсы загрузятся полностью. Но если он начнет выполняться (инициализация LightTableSorter.init()), пока в DOM еще не погрузилась таблица (точнее заголовки), то вот тут будет пусто, и никакие клики не забиндятся
var ths = document.getElementsByTagName('th');
Arr.forEach.call(ths, function (th) {th.onclick = onClickEvent;});

Вам надо провести "ручную" инициализацию после подгрузки таблицы. К сожалению, эта библиотека написана так, что вы не сможете вызывать код в Immediately-Invoked Function Expression (function (document) {})(document); без модификации библиотеки

Удалите этот код:
document.addEventListener('readystatechange', function(){
if (document.readyState === 'complete') LightTableSorter.init();
}, false);
а вместо него добавьте этот:
window.LightTableSorter = LightTableSorter;

Теперь вы можете после загрузки данных через load выполнить инициализацию. В момент вызова callback function таблица уже должна быть вставлена. Только оберните в jquery.ready
$(function() {
    $('#to_load').load('/stat/rating/profiles_rank/ #for_including1', function () {
        window.LightTableSorter.init();
    });
});
Ответ написан
Sergei_Erjemin
@Sergei_Erjemin Автор вопроса
Улыбайся, будь самураем...
Сделал свой велосипед по советам antonsnowy (спасибо ему огромное):

В sortable-table.js добавляем проверку на наличие блока #to_load:

if ($("div").is("#to_load"))
  window.LightTableSorter = LightTableSorter;
else
  document.addEventListener('readystatechange', function(){
    if (document.readyState === 'complete') LightTableSorter.init();
   }, false);


Это блок куда все подгружается. Если он есть, не инициализируем LightTableSorter.init(), а даем возможность передать ее через глобальные переменные. Инициализацию же осуществляем в скрипте где эта таблица подгружается:

<div id="to_load">Сюда подгружаем таблицу рейтинга</div>
<script type="text/javascript">
  $.getScript('/static/js/sortable-table.js');
  $('#to_load').load('/stat/rating/profiles_rank/ #for_including1',
    function () {
      window.LightTableSorter.init();
      });
</script>


Вроде все работает как надо.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
djQuery
@djQuery
"Кодируем помаленьку" ("Сказка о Тройке")))
Откройте страницу с хостинга, в браузере отобразите Исходный код страницы и пройдитесь по всем ссылкам, по которым грузятся скрипты. Возможно, что-то с путями.
Ответ написан
ThunderCat
@ThunderCat Куратор тега JavaScript
{PHP, MySql, HTML, JS, CSS} developer
Ваш ответ на вопрос

Войдите, чтобы написать ответ

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