javascript – Unknown error while creating your own function

Question:

I was writing my function to assign properties to pseudo-elements and noticed an unknown error. Assigning properties goes through adding lines to styleSheet.

When in the third case I call the function, for some reason the first element is painted blue, although I did not set color for it.

(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;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

<div class="element el0" name="MyName"></div>
<div class="element el0 el1" id="elem"></div>
<div>
  <div class="element el1"></div>
</div>

Why is the first element painted blue in the third case? How to fix it?


UPDATA:

If you print to the console the values ​​of the elements after each assignment, then very strange values ​​come out:

(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;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

<div class="element el0" name="MyName"></div>
<div class="element el0 el1" id="elem"></div>
<div>
  <div class="element el1"></div>
</div>

If you look at the console, then already at the first call of the function, element # 1 has a blue color and content # 3

Answer:

I think I found the problem:

element.pseudoElements[parameters.pseudoElement].properties = parameters.argument;

where parameters.argument is an object. When we make the first pass, we set it to the first and second elements. When on the second pass we change it for the second case, it changes for the first too.

And here is the proof and the solution

element.pseudoElements[parameters.pseudoElement].properties = Object.assign({}, parameters.argument);
Scroll to Top