Where to store temporary live – pixie123 Dec 20 '19 at 21:24

  • @pixie123 Have you heard of Angular, Vue.js etc...? Take a look at this link and see if its description sounds like what you're trying to do. https://angular.io/api/common/NgStyle While these framework make it easier to do this, you can certainly do them with pure javascript and DOM manipulation. Also how these things work is call data binding, as when the data changes (in this case your styles) it trigger the DOM to update the element. – noobius Dec 20 '19 at 21:32
  • @noobius Yes, while that could work, I already have most of the stuff built in jquery. Any solutions using jquery or pure js? – pixie123 Dec 20 '19 at 21:39
  • 6 Answers6

    6

    You can use the CSSStyleSheet APIs to generate a stylesheet in memory then use insert and delete methods to add or remove rules from the stylesheet at will. When the user is done modifying you could then pass the generated stylesheet back server side to save perm.

    Ref docs can be found here: https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet#Methods

    Compatability is IE9+ and all other modern browsers so it has good coverage.

    Quick and dirty example below.

    var style = (function() {
        // Create the <style> tag
        var style = document.createElement("style");
        // Add the <style> element to the page
        document.head.appendChild(style);
        return style;
    })();
    
    function AddRule(){
     //append rule from textbox to ss here
      style.sheet.insertRule(document.getElementById("cssIn").value, 0);
      document.getElementById("appliedRules").innerHTML = '';
      var rules = style.sheet.cssRules;
      for (var r in rules) {
        if(rules[r].cssText){
         document.getElementById("appliedRules").innerHTML += '<br>' +  rules[r].cssText;
        }
      }
    }
    //enable this to see your special prize in the console
    //console.log(style.sheet);
    <div class="test"> here we go</div>
    Add Rule: <input type="text" id="cssIn" value=".test {color:blue}">
    <button type="button" onClick="AddRule();">Add</button>
    
    <div id="appliedRules"></div>
    Travis Acton
    • 4,292
    • 2
    • 18
    • 30
    • way more elegant than my answer :) – Cave Johnson Dec 20 '19 at 23:54
    • This was my other question (on the other answer): What about if I need to make changes to a specific element? It will need to search through the to find the right element. Also, what if I need to add padding to a specific element. If padding does not exist, it needs to add the padding to the – pixie123 Dec 21 '19 at 00:41
    • @pixie123 Check the Ref docs in my link or enable the console write in my example. Each element has a selector attribute so you can quickly select from the array without explicit iteration. – Travis Acton Dec 21 '19 at 00:59
    4

    Here is a simple proof-of-concept that demonstrates how this can be done using pure javascript. Just click the save button to see the CSS in the textarea get applied to the page. The CSS is just stored as the input value of the textarea element. You can also make it more complex by using localStorage and an iframe or shadow dom so you only affect a "preview" pane. But this is just a demonstration.

    function saveStyles() {
        document.querySelector('#style-container').innerHTML = document.querySelector('#style-input').value;
    }
    #style-input {
      width: 100%;
      box-sizing: border-box;
      display: block;
      margin-bottom: 8px;
    }
    <style id="style-container"></style>
    <textarea id="style-input" rows="5">body{background:red;}</textarea>
    <button onclick="saveStyles()">Save</button>
    Cave Johnson
    • 6,499
    • 5
    • 38
    • 57
    • I can see that you saved it into `#styles`. But what about if I need to make changes to a specific element? It will need to search through the `` to find the right element. Also, what if I need to add padding to the textarea (as an example). If padding does not exist, it needs to add the padding to the ` – pixie123 Dec 20 '19 at 23:01
    • @pixie123 I am not sure I understand. The css code is still in the text box, you can just change whatever you need. If you need to add something, then just add it in the text box. The contents of the textbox overwrites whatever was in the `#styles` – Cave Johnson Dec 20 '19 at 23:11
    • 1
      @pixie123 Basically think of it as being "saved" in the textbox, not the `#styles` element. The `#styles` element is synced with the textbox every time you click save. Think of the textbox as the true source of the css data. – Cave Johnson Dec 20 '19 at 23:22
    • This is extremely clever +1 – pixie123 Dec 20 '19 at 23:33
    1

    Here's an alternative that puts the stylesheet into memory and loads it via a blob URL.

    This behaves a bit more like a real stylesheet than inline styles do in some edge cases, which may be desirable in some cases. It can also work on a webpage that blocks inline styles via a Content Security Policy (provided blob URL's are allowed).

    (function() {
    var styles = document.getElementById('styles');
    var save = document.getElementById('save');
    var link = null;
    
    function getLink() {
      if (!link) {
        link = document.createElement('link');
        link.rel = 'stylesheet';
        document.head.appendChild(link);
      }
      return link;
    }
    
    save.addEventListener('click', function() {
      var link = getLink();
      if (link.href) {
        URL.revokeObjectURL(link.href);
      }
      link.href = URL.createObjectURL(new Blob([styles.value], {type: 'text/css'}));
    });
    })();
    #styles {
        display: block;
        width: 95%;
    }
    <textarea id="styles" rows="5">body {
        background: green;
    }
    </textarea>
    <button id="save">Save</button>
    Alexander O'Mara
    • 58,688
    • 18
    • 163
    • 171
    1

    The answers here focus on the methods for building a stylesheet and adding css rules using common methods browsers provide as part of the DOM api. These are the underlying function calls that any UI framework on the web will use.

    But when you ask, "Where is this stored?". In a sense, you are asking how is the "state" managed. If you look at original post jQuery/web app building frameworks, like Backbone.js -- its motto was, "Get your model out of the DOM". So generally the "elements" of the ui-building tools will themselves be represented as component/models.

    If you look at view-centric frameworks, like React or Vue, more complex web apps will use a framework like Redux to handle "state", which is stored in single object store. This represents the current options of all the components on the page.

    So ultimately, a non-toy WYSIWYG web editor will likely treat each "element" as a component that renders its styles based on an inputted state. 

    This coupled with a controlled and predictable way to change state, allows for the managing of complex UI. For instance, the click method would trigger an action that acts like the name of an event handler, triggering the functions (in redux world, reducers), which ultimately changes the state, in our example, color of the element.

    The low-level calls to the DOM that facilitate this in a complex web-app/web-editor would be abstracted away.

    adamrights
    • 1,701
    • 1
    • 11
    • 27
    0

    Based on the discussion, I can suggest to use separate styles for each element id. Here is a sketch.

      <script>
    
      function setStyle(id, style_text) 
      {
        var style_id = "style_"  + id;
        var style_forId = "#" + id + " " + style_text;
    
        var styleDom = document.getElementById(style_id);
    
        if(!styleDom)
        {
            styleDom = document.createElement('style');
            styleDom.type = 'text/css';
            styleDom.id = style_id;
            styleDom.innerHTML = style_forId; 
            document.getElementsByTagName("head")[0].appendChild(styleDom);
        }
        else
        {
            styleDom.innerHTML = style_forId; 
        }
      }
    
      </script>
    
      <button id="myButton1" type="button" >My Button 1</button>
      <button id="myButton2" type="button" >My Button 2</button>
      <br>
      <button onclick="setStyle('myButton1', '{color:red}'); "> Set Red color for myButton1 </button>
      <br>
      <button onclick="setStyle('myButton2', '{color:red}'); "> Set Red color for myButton2 </button>
      <br>
      <button onclick="setStyle('myButton1', '{color:green}'); "> Set Green color for myButton1 </button>
      <br>
      <button onclick="setStyle('myButton2', '{color:green}'); "> Set Green color for myButton2 </button>
      <br>
    
    0

    Great Answers already just putting a different viewpoint out there.

    Simple Version

    Using CSSStyleSheet

    const style = document.createElement("style");
    document.head.appendChild(style);
    style.sheet.insertRule(`
      header {
        background: 'black'
      }
    `, 0);
    

    Real World

    I would take this simple idea and control it through a data structure like this.

    // A JS Object to control and modify CSS styles on.
    const css = {
        header: {
            background: 'black',
            border: '2px green solid',
            'font-size': '12px'
        }
    }
    
    // Converts JS Object to CSS Text (This is not battle tested)
    function objToCss(style) {
        return Object.entries(style).reduce((styleString, [propName, propValue]) => {
            propName = propName.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
            if (typeof propValue === 'object') {
                return `${styleString}${propName}{${objToCss(propValue)}}`;
            } else {
                return `${styleString}${propName}:${propValue};`;
            }
        }, '')
    }
    
    // Setup
    const style = document.createElement("style");
    document.head.appendChild(style);
    style.sheet.insertRule(objToCss(css), 0);
    
    // Updates
    css.header.border = '1px blue solid';
    style.sheet.replace(objToCss(css), 0);
    
    Michael Warner
    • 3,879
    • 3
    • 21
    • 45