Страницы

Поиск по вопросам

воскресенье, 1 декабря 2019 г.

Неизвестная ошибка при создании собственной функции

#javascript #jquery


Писал свою функцию для присвоения свойств к псевдоэлементам и заметил неведомую ошибку.
Присваивание свойств идёт через добавления строк в styleSheet.

Когда в третьем случае вызываю функцию, почему то красится в синий первый элемент,
хотя color для него я не ставил.



(function() {
  var setPseudoElement = function(parameters) {
    for (var element of parameters.elements.get()) {
      if (!element.pseudoElements) element.pseudoElements = {
        styleSheet: null,
        before: {
          index: null,
          properties: null
        },
        after: {
          index: null,
          properties: null
        }
      };

      var selector = (function() {
        if (element.id) {
          return '#' + element.id + '::' + parameters.pseudoElement;
        } else {
          var parentsList = $(element).parents().map(function() {
            return this.tagName.toLowerCase();
          }).get().reverse().join(' > ') + ' > ' + element.tagName.toLowerCase();

          var elementClass = element.classList.length ? '.' + $(element.classList).get().join('.')
: '';

          var elementAttributes = element.hasAttributes() ? $(element.attributes).get().map(function(className) {
            return className.nodeName !== 'class' ? className.nodeValue ? '[' + className.nodeName
+ '="' + className.nodeValue + '"]' : '[' + className.nodeName + '"]' : '';
          }).join('') : '';

          var elementNthChild = ':nth-child(' + ($(element).index() + 1) + ')';

          return parentsList + elementClass + elementAttributes + elementNthChild
+ '::' + parameters.pseudoElement;
        };
      })();

      if (!element.pseudoElements.styleSheet) {
        if (document.styleSheets[0]) {
          element.pseudoElements.styleSheet = document.styleSheets[0];
        } else {
          var styleSheet = document.createElement('style');

          document.head.appendChild(styleSheet);
          element.pseudoElements.styleSheet = styleSheet.sheet;
        };
      };

      if (element.pseudoElements[parameters.pseudoElement].properties !== null &&
element.pseudoElements[parameters.pseudoElement].index !== null) {
        element.pseudoElements.styleSheet.deleteRule(element.pseudoElements[parameters.pseudoElement].index);
      };

      if (typeof parameters.argument === 'object') {
        if (!element.pseudoElements[parameters.pseudoElement].properties && !element.pseudoElements[parameters.pseudoElement].index) {
          var newIndex = element.pseudoElements.styleSheet.rules.length || element.pseudoElements.styleSheet.cssRules.length
|| element.pseudoElements.styleSheet.length;

          element.pseudoElements[parameters.pseudoElement].index = newIndex;
          element.pseudoElements[parameters.pseudoElement].properties = parameters.argument;
        };

        var properties = '';

        for (var property in parameters.argument) {
          element.pseudoElements[parameters.pseudoElement].properties[property] =
parameters.argument[property];
        };

        for (var property in element.pseudoElements[parameters.pseudoElement].properties) {
          properties += property + ': ' + element.pseudoElements[parameters.pseudoElement].properties[property]
+ ' !important; ';
        };

        element.pseudoElements.styleSheet.addRule(selector, properties, element.pseudoElements[parameters.pseudoElement].index);
      } else if (parameters.argument !== undefined && parameters.property !== undefined) {

      } else if (parameters.argument !== undefined && parameters.property === undefined) {

      } else {
        console.error('Invalid values!');
        return false;
      };
    };
  };

  $.fn.cssBefore = function(argument, property) {
    setPseudoElement({
      elements: this,
      pseudoElement: 'before',
      argument: argument,
      property: property
    });
  };
})();

$(function() {
  // Случай 1
  $('.el0').cssBefore({
    'content': '"Новый \'before\'"',
    'color': 'green'
  });
  // Случай 2
  $('.el1').cssBefore({
    'content': '"Новый \'before\' №2"',
    'color': 'blue'
  });
  // Случай 3
  $('.el0').cssBefore({
    'content': '"Новый \'before\' №3"'
  });
});
.element {
  width: 480px;
  margin: 0 auto;
  border: 2px solid red;
}

.element:before {
  content: "Старый 'before'";
  color: orange;
}


Почему в третьем случае красится в синий первый элемент? Как это исправить? UPDATA: Если выводить в консоль значения элементов после каждого присваивания, то выходят очень странные значения: (function () { var i = 1; var setPseudoElement = function (parameters) { for (var element of parameters.elements.get()) { if (!element.pseudoElements) element.pseudoElements = {styleSheet: null, before: {index: null, properties: null}, after: {index: null, properties: null}}; var selector = (function () { if (element.id) { return '#' + element.id + '::' + parameters.pseudoElement; } else { var parentsList = $(element).parents().map(function () { return this.tagName.toLowerCase(); }).get().reverse().join(' > ') + ' > ' + element.tagName.toLowerCase(); var elementClass = element.classList.length ? '.' + $(element.classList).get().join('.') : ''; var elementAttributes = element.hasAttributes() ? $(element.attributes).get().map(function (className) { return className.nodeName !== 'class' ? className.nodeValue ? '[' + className.nodeName + '="' + className.nodeValue + '"]' : '[' + className.nodeName + '"]' : ''; }).join('') : ''; var elementNthChild = ':nth-child(' + ($(element).index() + 1) + ')'; return parentsList + elementClass + elementAttributes + elementNthChild + '::' + parameters.pseudoElement; }; })(); if (!element.pseudoElements.styleSheet) { if (document.styleSheets[0]) { element.pseudoElements.styleSheet = document.styleSheets[0]; } else { var styleSheet = document.createElement('style'); document.head.appendChild(styleSheet); element.pseudoElements.styleSheet = styleSheet.sheet; }; }; if (element.pseudoElements[parameters.pseudoElement].properties !== null && element.pseudoElements[parameters.pseudoElement].index !== null) { element.pseudoElements.styleSheet.deleteRule(element.pseudoElements[parameters.pseudoElement].index); }; if (typeof parameters.argument === 'object') { if (!element.pseudoElements[parameters.pseudoElement].properties && !element.pseudoElements[parameters.pseudoElement].index) { var newIndex = element.pseudoElements.styleSheet.rules.length || element.pseudoElements.styleSheet.cssRules.length || element.pseudoElements.styleSheet.length; element.pseudoElements[parameters.pseudoElement].index = newIndex; element.pseudoElements[parameters.pseudoElement].properties = parameters.argument; }; var properties = ''; for (var property in parameters.argument) { element.pseudoElements[parameters.pseudoElement].properties[property] = parameters.argument[property]; }; for (var property in element.pseudoElements[parameters.pseudoElement].properties) { properties += property + ': ' + element.pseudoElements[parameters.pseudoElement].properties[property] + ' !important; '; }; element.pseudoElements.styleSheet.addRule(selector, properties, element.pseudoElements[parameters.pseudoElement].index); console.log('Номер запуска: ' + Math.round(i / 2) + '; Номер присваивания элементу: ' + i); console.log({ 'Элемент 1': $('.el0:not(.el1)').get(0).pseudoElements, 'Элемент 2': $('.el0.el1').get(0).pseudoElements, 'Элемент 3': $('.el1:not(.el0)').get(0).pseudoElements }); i++; } else if (parameters.argument !== undefined && parameters.property !== undefined) { } else if (parameters.argument !== undefined && parameters.property === undefined) { } else { console.error('Invalid values!'); return false; }; }; }; $.fn.cssBefore = function (argument, property) { setPseudoElement ({ elements: this, pseudoElement: 'before', argument: argument, property: property }); }; })(); $(function() { // Случай 1 $('.el0').cssBefore({ 'content': '"Новый \'before\'"', 'color': 'green' }); // Случай 2 $('.el1').cssBefore({ 'content': '"Новый \'before\' №2"', 'color': 'blue' }); // Случай 3 $('.el0').cssBefore({ 'content': '"Новый \'before\' №3"' }); }); .element { width: 480px; margin: 0 auto; border: 2px solid red; } .element:before { content: "Старый 'before'"; color: orange; }
Если смотреть в консоль, то уже при первом вызове функции, элемент №1 имеет синий цвет и содержимое №3


Ответы

Ответ 1



Думаю что я нашел проблему: element.pseudoElements[parameters.pseudoElement].properties = parameters.argument; где parameters.argument — это объект. Когда мы делаем первый проход, мы задаем его для первого и второго элемента. Когда на втором проходе мы меняем его для второго случая, он меняется и для первого тоже. И вот пруф и решение element.pseudoElements[parameters.pseudoElement].properties = Object.assign({}, parameters.argument);

Ответ 2



Вот так поведение будет ожидаемым Вами. В чем изначально был подвох - пока разбираюсь, дополню в течении получаса. setTimeoutы стоят для удобства дебага, если что. Товарищи, хоть убейте, я не понимаю, почему оно работает и что вообще изначально не так. Смущает проскальзывание в теле цикла элемента parameters, так как, по логике, его внутри цикла быть не должно, работа ведется с конкретным element, но скорее всего, просто я не знаю jquery. (function() { var i = 1; var setPseudoElement = function(parameters) { for (var element of parameters.elements.get()) { if (!element.pseudoElements){ element.pseudoElements = { styleSheet: null, before: { index: null, properties: null }, after: { index: null, properties: null } };} if (element.pseudoElements) { element.pseudoElements.before.properties = null; element.pseudoElements.before.index = null; } var selector = (function() { if (element.id) { return '#' + element.id + '::' + parameters.pseudoElement; } else { var parentsList = $(element).parents().map(function() { return this.tagName.toLowerCase(); }).get().reverse().join(' > ') + ' > ' + element.tagName.toLowerCase(); var elementClass = element.classList.length ? '.' + $(element.classList).get().join('.') : ''; var elementAttributes = element.hasAttributes() ? $(element.attributes).get().map(function(className) { return className.nodeName !== 'class' ? className.nodeValue ? '[' + className.nodeName + '="' + className.nodeValue + '"]' : '[' + className.nodeName + '"]' : ''; }).join('') : ''; var elementNthChild = ':nth-child(' + ($(element).index() + 1) + ')'; return parentsList + elementClass + elementAttributes + elementNthChild + '::' + parameters.pseudoElement; }; })(); if (!element.pseudoElements.styleSheet) { if (document.styleSheets[0]) { element.pseudoElements.styleSheet = document.styleSheets[0]; } else { var styleSheet = document.createElement('style'); document.head.appendChild(styleSheet); element.pseudoElements.styleSheet = styleSheet.sheet; }; }; if (element.pseudoElements[parameters.pseudoElement].properties !== null && element.pseudoElements[parameters.pseudoElement].index !== null) { element.pseudoElements.styleSheet.deleteRule(element.pseudoElements[parameters.pseudoElement].index); }; if (typeof parameters.argument === 'object') { if (!element.pseudoElements[parameters.pseudoElement].properties && !element.pseudoElements[parameters.pseudoElement].index) { var newIndex = element.pseudoElements.styleSheet.rules.length || element.pseudoElements.styleSheet.cssRules.length || element.pseudoElements.styleSheet.length; element.pseudoElements[parameters.pseudoElement].index = newIndex; element.pseudoElements[parameters.pseudoElement].properties = parameters.argument; }; var properties = ''; for (var property in parameters.argument) { element.pseudoElements[parameters.pseudoElement].properties[property] = parameters.argument[property]; }; for (var property in element.pseudoElements[parameters.pseudoElement].properties) { properties += property + ': ' + element.pseudoElements[parameters.pseudoElement].properties[property] + ' !important; '; }; element.pseudoElements.styleSheet.addRule(selector, properties, element.pseudoElements[parameters.pseudoElement].index); console.log('Номер запуска: ' + Math.round(i / 2) + '; Номер присваивания элементу: ' + i); console.log({ 'Элемент 1': $('.el0:not(.el1)').get(0).pseudoElements, 'Элемент 2': $('.el0.el1').get(0).pseudoElements, 'Элемент 3': $('.el1:not(.el0)').get(0).pseudoElements }); i++; } else if (parameters.argument !== undefined && parameters.property !== undefined) { } else if (parameters.argument !== undefined && parameters.property === undefined) { } else { console.error('Invalid values!'); return false; }; }; }; $.fn.cssBefore = function(argument, property) { setPseudoElement({ elements: this, pseudoElement: 'before', argument: argument, property: property }); }; })(); $(function() { // Случай 1 $('.el0').cssBefore({ 'content': '"Новый \'before\'"', 'color': 'green' }); setTimeout(() => { // Случай 2 $('.el1').cssBefore({ 'content': '"Новый \'before\' №2"', 'color': 'blue' }); }, 3000); // Случай 3 setTimeout(() => { $('.el0').cssBefore({ 'content': '"Новый \'before\' №3"' });}, 6000); }); .element { width: 480px; margin: 0 auto; border: 2px solid red; } .element:before { content: "Старый 'before'"; color: orange; }


Комментариев нет:

Отправить комментарий