4

There is a need to update css to dynamic value and I am not sure what's the best approach to it.

<div id="app" style="zoom: 0.XX;">
  ...
</div>

The zoom level will trigger based on window resize and the app will zoom according. I loaded this app into cordova and have it run within iPAD, then I realize the font-size needs to be adjusted to the same as zoom level using "-webkit-text-size-adjust" in order for it to not break the design layout.

My challenge is to set the css dynamically like this:

#app * {
  -webkit-text-size-adjust : nn%  
}

Where nn is the zoom X 100 + '%'

I have tried:

1) Set the style on the app div, but this doesn't help to apply to inner elements

 <div id="app" style="zoom: 0.XX; -webkit-text-size-adjust: XX%">

2) Use javascript to set to all inner nodes, but not only I think this is less efficient, but it won't get trigger if my window doesn't resize, that means if I navigate to other pages, this logic won't get called.

REF: https://stackoverflow.com/questions/25305719/change-css-for-all-elements-from-js

let textSizeAdjust = function(zoom) {
  let i,
  tags = document.getElementById("app").getElementsByTagName("*"),
  total = tags.length;

  for ( i = 0; i < total; i++ ) {
    tags[i].style.webkitTextSizeAdjust = (zoom * 100) + '%';
   }
}

3) I tried using javascript, and most likely they are technically incorrect because querySelector return null.

document.querySelector('#app *').style.webkitTextSizeAdjust = zoom *100 + '%';

document.querySelector('#app').querySelector('*').style.webkitTextSizeAdjust = zoom * 100 + "%";

Ultimate, I believe I need to dynamically create the css, for the browser to apply this setting to the DOM:

#app * {
   -webkit-text-size-adjust: nn    
}

Please let me know if this is the right, or how to use javascript to create the above css and change the value dynamically?

Alocus
  • 1,860
  • 3
  • 21
  • 32

2 Answers2

4

CSS Variables


Requirements


HTML


Each form control that has numerical data should have:

  1. value={a default, don't leave it blank}

  2. class='num'

  3. data-unit={unit of measurement or a single space}

  4. The select/option tag should have the selected attribute


CSS

CSS Variable Signature: propertyName: var(--propertyValue)


// Declare CSS Variables at the top of a stylesheet
:root {
  --mx0: 50px;
  --my0: 50px;
  --rz0: 1.0;
  --zm0: 1.0;
  --sp0: 360deg;
}

JavaScript


There's step by step details commented in the JavaScript Demo. Here's the most important statement in the code:

CSSStyleDeclaration      CSS Variable

                           

`ele.style.setProperty(`--${node.id}`,

${node.valueAsNumber}${node.dataset.unit})

                            

HTMLInputElement      DataSet API


Demo 1


// Reference form#UI
var ui = document.forms.UI;

// Register form#UI to change event
ui.addEventListener('change', setCSS);

// Callback passes Event Object
function setCSS(e) {

  // Collect all form controls of form#UI into a NodeList
  var fx = ui.elements;

  // Reference select#pk0
  var pk0 = fx.pk0;

  // Get select#pk0 value
  var pick = pk0.options[pk0.selectedIndex].value

  // if the changed element has class .num...
  if (e.target.className === 'num') {

    // Reference Event Target
    var tgt = e.target;

    // Then reference is by its #id
    var node = document.getElementById(tgt.id);

    // DOM Object to reference either html, square, or circle
    var ele;

    /* Determine which tag to test on: html (affects everything),
    || #sQ<uare> and #ciR<cle> shapes.
    */
    switch (pick) {
      case "rT":
        ele = document.documentElement;
        break;
      case "sQ":
        ele = document.getElementById('sQ');
        break;
      case "cR":
        ele = document.getElementById('cR');
        break;
      default:
        break;
    }
    /* Sets a target element's Transform:
    || translateXY, scale, and rotate
    */
    ele.style.setProperty(`--${node.id}`, `${node.valueAsNumber}${node.dataset.unit}`);
  }
}
/* Declare CSS Variables on the :root selector at the top of sheet
   All CSSVar must be prefixed with 2 dashes: --
*/

:root {
  --mx0: 50px;
  --my0: 50px;
  --rz0: 1.0;
  --sp0: 360deg;
}

.set {
  border: 3px ridge grey;
  border-bottom-left-radius: 6px;
  border-bottom-right-radius: 6px;
  padding: 5px;
}


/* The var() function's signature is:
   propertyName: var(--propertyValue)
*/

#sQ {
  position: relative;
  background: rgba(0, 100, 200, 0.3);
  width: 50px;
  height: 50px;
  transform: translateX(var(--mx0)) translateY(var(--my0)) scale(var(--rz0)) rotate(var(--sp0));
  border: 3px ridge grey;
  z-index: 1;
  transition: all 1s ease;
}

#cR {
  position: relative;
  background: rgba(200, 100, 0, 0.3);
  width: 50px;
  height: 50px;
  transform: translateX(var(--mx0)) translateY(var(--my0)) scale(var(--rz0)) rotate(var(--sp0));
  border: 3px ridge grey;
  border-radius: 50%;
  transition: all 1s ease;
}

#sQ::before {
  content: '\1f504';
  text-align: center;
  font-size: 2.25rem;
  transform: translate(1px, -8px)
}

#cR::after {
  content: '\1f3b1';
  text-align: center;
  font-size: 2.25rem;
}

input,
select {
  display: inline-block;
  width: 6ch;
  font: inherit;
  text-align: right;
  line-height: 1.1;
  padding: 1px 2px;
}

select {
  width: 9ch
}

.extension {
  overflow-y: scroll;
  overflow-x: auto;
  min-height: 90vh;
}


/* For debugging on Stack Snippets */


/*.as-console-wrapper {
  width: 25%;
  margin-left: 75%;
  min-height: 85vh;
}*/
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <style></style>
</head>

<body>
  <!--
HTML Requirements
 Each form control that has numerical data should have:
 1. value={a default, don't leave it blank}
 2. class='num'
 3. data-unit={unit of measurement or a single space}
 4. The select/option tag should have the selected attribute
-->
  <form id='UI'>
    <section class='set'>

      <label>X: </label>
      <input id='mx0' class='num' type='number' min='-350' max='350' value='50' step='10' data-unit='px'>

      <label>Y: </label>
      <input id='my0' class='num' type='number' min='-350' max='350' value='50' step='10' data-unit='px'>

      <label>Size: </label>
      <input id='rz0' class='num' type='number' min='0' max='5' value='1' step='0.1' data-unit=' '>

      <label>Spin: </label>
      <input id='sp0' class='num' type='number' min='0' max='1440' value='360' step='180' data-unit='deg'>

      <label>Pick: </label>
      <select id='pk0' class='num'>
        <option value='rT' selected>Root</option>
        <option value='sQ'>Square</option>
        <option value='cR'>Circle</option>
      </select>

    </section>
  </form>
  <section class='set extension'>
    <div id='sQ' class='test shape' width="50" height="50"></div>
    <div id='cR' class='test shape' width="50" height="50"></div>
  </section>
</body>

</html>

Update

This update is specifically for OP, so this may be of help or not for other users.

Deno 2


:root {
  --opc: 0;
  --zoom: 1;
}

.fc {
  display: inline-block;
  width: 18ch;
  margin:0 0 10px 0
}

#app * {
  opacity: var(--opc);
  transform: scale(var(--zoom));
}
<!doctype html>
<html>

<head>
  <meta charset='utf-8'>

</head>

<body>


  <form id='app' action='https://httpbin.org/post' method='post' target='view'>
    <fieldset class='sec'>
      <legend>App of Mystery</legend>
      <input id='A0' name='A0' class='fc' type='text' placeholder='User Name'>
      <input id='A1' name='A1' class='fc' type='password' placeholder='Password'>
      <input type='submit'>
      <input type='reset'>
      <input id='zBtn' type='button' value='Zoom'>
      <iframe name='view' frameborder='1' width='100%'></iframe>
    </fieldset>

  </form>
  <script>
    var node = document.querySelector('#app *');
    var zBtn = document.getElementById('zBtn');
    var flag = false;
    
    document.addEventListener('DOMContentLoaded', function(e) {
      node.style.setProperty("--opc", "0.5");
    });

    document.addEventListener('click', function(e) {
      node.style.setProperty("--opc", "1");
    });
    
    zBtn.addEventListener('click', function(e) {
      if (flag) {
        flag = false;
        node.style.setProperty("--zoom", "1");
      } else {
        flag = true;
        node.style.setProperty("--zoom", "1.25");
      }
    });

   
  </script>
</body>

</html>
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • wow. this is really cool and thank you very much for taken the time to help explain this. I didn't even know I could have css variable. Nonetheless, my goal is to set css for "#app *" and I still don't think I can do this via javascript as I don't know what css selector to use. – Alocus May 22 '18 at 17:34
  • You should read your question again: *"Ultimate, I believe I need to dynamically create the css, for the browser to apply this setting to the DOM:"*: `#app { -webkit-text-size-adjust: nn }` If you read my answer, then you would a ruleset of `#app { -webkit-text-size-adjust: var(--nn) }` and `:root {--nn:100%}`. I believe what happened is: [XY Problem](http://xyproblem.info/) – zer00ne May 22 '18 at 19:46
  • @Alocus BTW, injecting a style block for dynamic CSS is a good simple solution. I've used it at work for quick single page patches. But AnilRedshift's example is triggered on a submit event which requires: 1. a`
    ` tag, 2. `` or `` 3. Most likely a server to send data to `
    `. Of course that's the most common requirements involving a submit event. Did you plan on triggering on a resize event instead? Do you have media queries in your CSS?
    – zer00ne May 22 '18 at 19:59
  • @Alocus if you get stuck, ask another question and notify me by comment here. I just had an epiphany. `matchMedia` and `CSSRules` is what you actually need. The solutions offered on this confusing question are not going to help with **point #2** of your question. It's the most challenging and necessary point you've made. – zer00ne May 22 '18 at 20:11
  • #app * { -webkit-text-size-adjust: nn } could I have #app * as a selector and use javascript to select it? My problem is selecting "#app *" with javascript. Using var node = document.getElementById(tgt.id) doesn't work for me. – Alocus May 23 '18 at 19:01
  • `tgt = e.target` that's the clicked element or hovered over `tgt.id` is a string representing the id of clicked, hovered over, or any applicable event element. That line works in the context of a click event. So it would simply be `var node = document.getElementById('app');` since `#app` isn't being clicked at all I assuming. Just guessing because the nature of app is nebulous. – zer00ne May 23 '18 at 23:42
  • 1
    Just realized you are using `#app *` as a selector. I recommend that you never do that...but not seeing the whole layout who's to say? `var node = document.querySelectorAll('#app *')` That collects **every tag under `#app`**. You'll have to iterate through each tag in order to be effective. You need to research that CSS property and find out if it is inheritable, if so then do not use the `*`. A inheritable CSS property should move on to `#app`'s descendants. To verify, F12 and look at the computed tab for a child element of `#app` and see if it inherited that property or not. – zer00ne May 23 '18 at 23:52
  • I will have to try your code `var node = document.querySelector('#app *');` because for me the code return null. I understand the impact of querySelectorAll(#app *), that's why I decided to not loop through all elements but to dynamically change the css so the browser can handle the update. Thanks for clarification. I will play with your code. This has been very helpful. – Alocus May 24 '18 at 14:46
  • In my question, I tried (3) `var node = document.querySelector('#app *');` which gave me null and I tried (2) `var node = document.querySelectorAll('#app *')` which I found out not effective and won't work in some cases. – Alocus May 24 '18 at 14:50
  • if you got nul from: `var node = document.querySelector('#app *');` then either: 1. `#app` doesn't exist. or 2. `#app` is empty. I can't come up with a 3rd reason because there's only 2 and those 2 reasons would be pretty obvious. Please correct me if I'm wrong....BTW be aware that using `#app *` selector will only target the first child of `#app`. The reason why I used it in Demo 2 is because of the simplistic layout and inheritance, – zer00ne May 24 '18 at 16:17
1

I don't have much knowledge about -webkit-text-size-adjust

However, this should work for creating a dynamic stylesheet and inserting it:

I have added code to dynamically update it as well

const form = document.getElementById('colorChooser');
form.addEventListener('submit', (e) => {
  e.preventDefault();
  color = document.getElementById('colorInput').value;
  const style = document.getElementById('colorStyle');
  style.innerHTML = `#app * {
    background-color: ${color};
  }`;
});
const style = document.createElement('style');
style.id = 'colorStyle';
style.type = 'text/css';
style.innerHTML = `#app * {
  background-color: red;
}`;

document.head.appendChild(style);
#app {
  margin-bottom: 10px;
}
#inner {
  width: 50px;
  height: 50px;
  background-color: black;
}
<div id="app">
  <div id="inner"></div>
</div>
<form id="colorChooser">
<input id="colorInput" type="text" placeholder="red" />
<input type="submit" value="Update color"/>
</form>
AnilRedshift
  • 7,937
  • 7
  • 35
  • 59
  • Thanks, but my use case is to be able to edit the style dynamically. In your example, red needs to be changed to blue, black or yellow based on condition. Perhaps I should remove the style sheet, update the value and add it again using this strategy? – Alocus May 18 '18 at 17:56
  • @Alocus yes, it's just a DOM element, so you can change the innerHTML whenever. I've updated the answer accordingly – AnilRedshift May 18 '18 at 18:16
  • I create a style and attached to head, then I just update the innerHTML of the style dynamically and it works. Thanks @AnilRedshift – Alocus May 22 '18 at 19:02