4

I wrote my own function for assigning properties to pseudo-elements and noticed an unknown error. Assigning properties is done by adding lines to the styleSheet.

When in the third case I call a function, why is the first element painted in blue, although I did not put 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() {
  // Case 1
  $('.el0').cssBefore({
    'content': '"New \'before\'"',
    'color': 'green'
  });
  // Case 2
  $('.el1').cssBefore({
    'content': '"New \'before\' №2"',
    'color': 'blue'
  });
  // Case 3
  $('.el0').cssBefore({
    'content': '"New \'before\' №3"'
  });
});
.element {
  width: 480px;
  margin: 0 auto;
  border: 2px solid red;
}

.element:before {
  content: "Old '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 in blue in the third case? How to fix it?


UPDATA:

If display the values of the elements in the console 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('Launch number: ' + Math.round(i / 2) + '; Assignment number to element: ' + i);
                console.log({
                    'Element 1': $('.el0:not(.el1)').get(0).pseudoElements,
                    'Element 2': $('.el0.el1').get(0).pseudoElements,
                    'Element 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() {
  // Case 1
  $('.el0').cssBefore({
    'content': '"New \'before\'"',
    'color': 'green'
  });
  // Case 2
  $('.el1').cssBefore({
    'content': '"New \'before\' №2"',
    'color': 'blue'
  });
  // Case 3
  $('.el0').cssBefore({
    'content': '"New \'before\' №3"'
  });
});
.element {
  width: 480px;
  margin: 0 auto;
  border: 2px solid red;
}

.element:before {
  content: "Old '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, the first element for some reason already on the first call of the function has a blue color and content number 3

0

2 Answers 2

1

The problem is here

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

First and second elements shares the same parameters.argument object and when you change it for the second element (second run) it will be changed for the first element.

You can avoid it with

element.pseudoElements[parameters.pseudoElement].properties = {};

or

element.pseudoElements[parameters.pseudoElement].properties = Object.assign({}, parameters.argument);
Sign up to request clarification or add additional context in comments.

Comments

0

You had a little error in the code, and a bad reference to the cols el0 el1 and el2, I mean, el2 doesn't exist, but you can create it and give it a colour as you want.

But if you don't specify none color, it will take the last colour you used

(function() {
  var f_cssPseudoElement = function(o_parameters) {
    o_parameters.element.each(function() {
      var $_target = $(this);

      var s_selector = $_target.parents().map(function() {
        return this.tagName.toLowerCase();
      }).get().reverse().join(' > ') + ' > ' + $_target.get(0).tagName.toLowerCase() + ':nth-child(' + ($_target.index() + 1) + ')::' + o_parameters.pseudoElement;

      if (!$_target.data('__pseudoElements__')) $_target.data('__pseudoElements__', {
        StyleSheet: null,
        after: {
          index: null,
          properties: null
        },
        before: {
          index: null,
          properties: null
        }
      });

      var o_pseudoElements = $_target.data('__pseudoElements__');

      // I create a body to save styles
      if (!o_pseudoElements.StyleSheet) {
        if (document.styleSheets[0]) {
          o_pseudoElements.StyleSheet = document.styleSheets[0];
        } else {
          var e_StyleSheet = document.createElement('style');

          document.head.appendChild(e_StyleSheet);

          o_pseudoElements.StyleSheet = e_StyleSheet.sheet;
        };

        $_target.data('__pseudoElements__', o_pseudoElements);
      };

      if (typeof o_parameters.undefinedArgument === 'object') {
        /* === Function start ===*/
        if (o_pseudoElements[o_parameters.pseudoElement].properties === null && o_pseudoElements[o_parameters.pseudoElement].index === null) {
          var index = o_pseudoElements.StyleSheet.rules.length || o_pseudoElements.StyleSheet.length;

          o_pseudoElements[o_parameters.pseudoElement].index = index;
          o_pseudoElements[o_parameters.pseudoElement].properties = o_parameters.undefinedArgument;
        } else {
          o_pseudoElements.StyleSheet.deleteRule(o_pseudoElements[o_parameters.pseudoElement].index);
        };

        var rule = '';

        // I add new properties
        for (var property in o_parameters.undefinedArgument) {
          o_pseudoElements[o_parameters.pseudoElement].properties[property] = o_parameters.undefinedArgument[property];
        };

        // Create a new CSS
        for (var property in o_pseudoElements[o_parameters.pseudoElement].properties) {
          rule += property + ': ' + o_pseudoElements[o_parameters.pseudoElement].properties[property] + ' !important; ';
        };

        o_pseudoElements.StyleSheet.addRule(s_selector, rule, o_pseudoElements[o_parameters.pseudoElement].index);

        $_target.data('__pseudoElements__', o_pseudoElements);

        return o_parameters.element;
        /* === End of function ===*/
      } else if (o_parameters.undefinedArgument !== undefined && o_parameters.property !== undefined) {
        // Unreported part
      } else if (o_parameters.undefinedArgument !== undefined && o_parameters.property === undefined) {
        // Unreported part
      } else {
        console.error('Invalid values!');
        return false;
      };
    });
  };

  $.fn.cssBefore = function(s$o_argument, s_property) {
    f_cssPseudoElement({
      element: $(this),
      pseudoElement: 'before',
      undefinedArgument: s$o_argument,
      property: s_property
    });

    return false;
  };
  $.fn.cssAfter = function(s$o_argument, s_property) {
    f_cssPseudoElement({
      element: $(this),
      pseudoElement: 'after',
      undefinedArgument: s$o_argument,
      property: s_property
    });

    return false;
  };
})();

$(function() {
  // Case 1
  $('.el0').cssBefore({
    'content': '"New \'before\'"',
    'color': 'red'
  });
  // Case 2
  $('.el1').cssBefore({
    'content': '"New \'before\' №2"',
    'color': 'orange'
  });
  // Case 3
  $('.el2').cssBefore({
    'content': '"New \'before\'"',
    'color': 'blue'
  });
});
.element {
  width: 480px;
  margin: 0 auto;
  border: 2px solid red;
}

.element:before {
  content: "Old 'before'";
  color: orange;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

<div class="element el0"></div>
<div class="element el1"></div>
<div class="element el2"></div>

5 Comments

But why does it take the last color? Is it somewhere saved?
@Yuri mm, good question, sorry I don't have an answer to that :/
@Yuri I read your comment: jsfiddle.net/2chso9zz take a look at the final last line of css .element:nth-child(3):before, switch the numbers between 1, 2, 3 and look the colors :), you will find your answer, I hope
I do not quite understand
@Yuri Mmm, look the last line of these three links: jsfiddle.net/5ouw2yz1 , jsfiddle.net/ptLcspea , jsfiddle.net/vcLn41g5 . And how the colors change, it's about how are they referenced

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.