Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

[Question] Colour reacting input fields without JS?

1537925330

Edited 1537925396
Leothedino
Sheet Author
So, let's say you have a stat with two input boxes, the first being the stat itself, the second the stat when modified. Is there a way, within the limitations of roll20's custom sheet maker, to code that second box to change colour depending on the value going above or below the first? Curious if this is possible, but i'll provide a picture as an example.
1537926821
Finderski
Plus
Sheet Author
Compendium Curator
It's possible, but you may need a sheet worker to do it...If the box on the right is colored depending on it's relation to the box on the left (e.g. higher than = green and lower than = red), I'd use a sheet worker  that watches for changes in those two boxes, and when a change occurs, evaluate them and if the value is higher, have the sheet worker set the value of a "green" checkbox that can then be used to set the background color of the box to green.  If the value is less than, then the sheet worker would make sure that the green box is not checked and check a red checkbox.  If the values are equal, uncheck both checkboxes...
Oh this is a good idea, kinda like faking it with a background colour? I'll have to start working on this. Getting used to Sheet Worker, but all of the code-speak is out there on the web, so i'll have an experiment with what I can dig up. 
1537941871

Edited 1537941888
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Take a look at how I handle buff/penalty styling in the Starfinder sheet or how the 5e by Roll20 sheet handles same for some ideas on this. On mobile and heading to bed, but I can post some code tomorrow if the code doesn't make sense (I need to be better about annotating my code).
1537964927

Edited 1537965533
Leothedino
Sheet Author
Scott C, i'll bring up those sheets, thanks! And yeah, if you have any code lying around, i'd love an example. Of course, don't write anything up unless you want to, it's just a theory I want to test and time is precious.
1538058127

Edited 1538058280
Leothedino
Sheet Author
Scott C. i've been through those two sheets, and perhaps i'm missing something, but I don't see where these sheets show similar examples of what I was asking about above.  They style nicely when you click them, sure, but my query was about an event taking place if it raised or lowered under the max value of another.
1538062080

Edited 1538062976
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Hi Leo, The Starfinder sheet does this for buff/penalty display. I'll give some modified example excerpts below: HTML <input class='base-attr attr' type='number' title='@{strength}' name='attr_strength_base' value='10' > <input class='mod-buff' type='hidden' name='attr_strength_change' value='0'> <!-- This is the driver of the change, it's important that this be type="hidden". This attribute is set to -1, 0, or 1 depending on whether the total strength is buffed (greater than strength_base), penalized (less than strength_base), or unaffected (equals strength_base)--> <span class='modified-attr attr input-field' readonly='true' type='number' title='@{strength}' name='attr_strength' value='10'>10</span> <!-- This has to be a span as not all browsers support the use of the :after and :before pseudoelements on inputs (e.g. chrome does, but firefox doesn't) --> CSS .sheet-mod-buff[value*="1"] ~ .sheet-modified-attr {/*if an mod-buff class entity has a value of 1, change how the modified-attr class entity(ies) following it are displayed*/ color:#2dafeb; font-weight:900 !important; text-shadow:none; font-family:Contrail One; } .sheet-mod-buff[value*="1"] ~ .sheet-modified-attr:after{/*as above, but adds some decoration to the modified-attr using the :after pseudoelement*/ font-family:Pictos; content:'{'; position:absolute; } /*The two entries below are the same as above, but for penalties*/ .sheet-mod-buff[value*="-1"] ~ .sheet-modified-attr { color:#fa893a; font-weight:900 !important; text-shadow:none; font-family:Contrail One; } .sheet-mod-buff[value*="-1"] ~ .sheet-modified-attr:after{ font-family:Pictos; content:'{'; position:absolute; transform:rotate(180deg); transform-origin:center 45%; } So, now we've got our html and css pattern setup, now we just need to decide what to set the strength_change attribute to. We'll need to use sheetworkers (aka javascript) for this: JS <script type='text/worker'>//You should only have a single text/worker script section. Multiple of them will crash. You can however put multiple functions/scripts in a single text/worker entity on('change:strength_base',(event)=>{//When the user changes strength_base, do something     const setObj = {};//initializing the object we'll be storing all the attribute values in.     getAttrs(['strength_base','strength_buff','strength_penalty'],(obj)=>{//get the current values of the listed attributes. Returns as an object (aka {strength_base:14,strength_buff:2,strength_penalty:-1})         setObj.strength = (obj.strength_base*1||0)+(obj.strength_buff*1||0)-(obj.strength_penalty*1||0);//Set the strength to be equal to the base value +/- the buffs/penalties         if(setObj.strength > (obj.strength_base*1||0)){//if strength is higher than the base, it's buffed and strength_change should be set to 1             setObj.strength_change = 1;         }else if(setObj.strength <(obj.strength_base*1||0)){//if strength is higher than the base, it's penalized and strength_change should be set to -1             setObj.strength_change = -1;         }else{//Otherwise, set strength_change to 0 as strength_base and strength are equal             setObj.strength_change = 0;         }         setAttrs(setObj,{silent:true});//Set the attributes. This is done asynchronously. The {silent:true} is an optional argument that means these changes won't trigger further sheetworkers. Cascading sheetworkers are generally a bad idea.     }); }); </script> And there you have it. The above is a simplified version of what the starfinder sheet does, but should be much easier to follow since it's not spread over more than 10k lines of code. EDIT: Realized i was still using the ~ selector. That's a hold over from when I used hidden radio buttons for the _change attribute. With the type='hidden' attribute instead, you could switch to using the + selector. EDIT the 2nd: And of course if you aren't adding text decoration, then you don't need the modified attribute to be a span. If you are planning on releasing the sheet to the public though, I would encourage text decoration as it helps with accessibility so that color-blind users can differentiate between the buffed (green) and penalized (red) states. Since there are a wide range of color blindness disorders, this is easier than trying to find a color combination that no one is blind to.
1538102481

Edited 1538102623
Leothedino
Sheet Author
Damn, I never thought about people who are colour blind, nice catch there! Cheers Scott, pretty late here (4am, whoops...) will go through this tomorrow and try it out, I appreciate the time you took to make this comprehensive. Thanks man! 
1538103431
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
No worries, and let me know if you have any questions. Accessibility is something that worries me in the sheets I work on. I know that they have issues currently and probably always will, but by the same token, I'll always be looking for ways to improve them. As for the comprehensiveness, it's not a big deal; most of that is straight copy/paste from the Starfinder Official sheet, so was pretty easy to give an example.
1538136470

Edited 1538136515
Leothedino
Sheet Author
I no doubt will have one or two questions, but only one at the moment. When you set the classes for the HTML, you put it like: class='modified-attr attr input-field' I thought a class had to be all connected or else it wouldn't register. What is 'attr' and 'input-field' serving there, I can't seem to see where it connects in the CSS.  EDIT: I understand this is a little off-topic, but it still has me curious :P 
1538139909

Edited 1538139923
GiGs
Pro
Sheet Author
API Scripter
Those are three different classes. You can have multiple classes assigned to the same entity, and target them with different CSS rules. As a simple example you could have for instance: <label class='bold underlined' > A Bold, underlined label</label> <label class='underlined red' > An underlined, red statement</label> <label class='bold' > Some bold text</label> // CSS /// .sheet-red { color: red } .sheet-bold { font-weight: bold } .sheet-underlined { text-decoration: underline } The ability to mix and match CSS rules in this way makes it very flexible.
1538145255
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Yep, those classes aren't used for the example. They are used for other styling in the sheet.
1538161566

Edited 1538161651
Leothedino
Sheet Author
I think going over this, what I planned to do won't work. The examples are brilliant, but as you state, Firefox isn't good with :after and :before input pseudoelements, which is what I was going for. I'd need an input where the style of that field changed depending on the relationship it had with another input. It's been an excellent learning curve for me, mind. I went from barely knowing how your example worked, to getting a generally good idea. So thanks, I appreciate it. :D  
1538161959
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Well, there's some ways around that. I didn't include them in my examples to make them simpler, but you can create a span/input combo so that you can get the best of both worlds. I'll try to put a demo of that together.
I'm cooking away at something here, too. Trying to see if perhaps I can make the input transparent and have it sit over something of an element that changes color (my technical lingo is terrible, haha). I appreciate the help, thank you.
1538162951
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Yep, that's pretty much what the technique I mentioned is.
Scott, can I just cover something with you? A few bits (in bold)  of this Sheet Worker demo are foreign to me, and just not clicking.  on('change:strength_base',(event)=>{      const setObj = {};     getAttrs(['strength_base','strength_buff','strength_penalty'], (obj)=>{          setObj.strength = (obj.strength_base*1||0)+(obj.strength_buff*1||0)-(obj.strength_penalty*1||0);         if(setObj.strength > (obj.strength_base*1||0)){             setObj.strength_change = 1;         }else if(setObj.strength <(obj.strength_base*1||0)){             setObj.strength_change = -1;         }else{             setObj.strength_change = 0;         }         setAttrs(setObj,{silent:true});     }); }); So, to clarify: Const setObj = {}  Is this asking 'setObj' to store data, and if so, why does it not define anything between those bracers? (obj)=>{ From my understanding '=>' means 'equal or greater than', so why is it here before we've got to the 'if statement' further down the Sheet Worker? setObj.strength = (obj.strength_base*1||0)+(obj.strength_buff*1||0)-(obj.strength_penalty*1||0); In all out honesty on this one, I have no idea what this part is doing. I assume you're telling the sheet worker that ' setObj.strength = base + buff - penalty ' but my brain can't work out how that helps in the overall code.  Sorry if these questions are a bit Lesson One level, I keep having these moments where I think "wait, no, I get it-... oh no, no I don't". 
1538168093

Edited 1538168281
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Heh, no worries, I kind of failed to explain what was going on there in the first demo. So, to take your questions in order: So, to clarify: Const setObj = {}  Is this asking 'setObj' to store data, and if so, why does it not define anything between those bracers? So, this line is defining a constant named setObj as an empty javascript object. We can then add key:value  pairs to it later on in the code. (obj)=>{ From my understanding '=>' means 'equal or greater than', so why is it here before we've got to the 'if statement' further down the Sheet Worker? This is something that I know as fat arrow syntax. I'm sure it has an actual technical name though. It's essentially a shortcut for doing: function(some, variables) = {} In addition to being a shortcut for the above, it doesn't establish it's own scope either. Also, in javascript greater or equal than would be >=. setObj.strength = (obj.strength_base*1||0)+(obj.strength_buff*1||0)-(obj.strength_penalty*1||0); In all out honesty on this one, I have no idea what this part is doing. I assume you're telling the sheet worker that ' setObj.strength = base + buff - penalty ' but my brain can't work out how that helps in the overall code.  So, yes this is just doing the math to add all the parts together (note that _buff and _penalty versions of the attribute are notional, and only there for the demo). The part that is probably thowing you is that each argument is wrapped in parentheses and the  *1||0 . The *1||0 are there to ensure that the values are transferred to numbers, and the parentheses are there to properly scope where the or (aka ||) ends it's effect. Even if you specify the input as type='number', the actual attributes are still just plain text. Sometimes getAttrs seems to return the values as numbers, and sometimes as strings. I'm not sure what causes the difference precisely, but I think it has to do with what has been set by a sheetworker vs. set by a user. You could use something like parseInt() or parseFloat() instead of the *1, but you'd still need the ||0 to ensure it registered as 0 (or your default value of choice) if the parse doesn't work. The reason I use *1 is that it should be faster than parseInt() or parseFloat(), and it handles decimals better than parseFloat().
1538168759

Edited 1538168893
Leothedino
Sheet Author
Ah, right! (And wow, you were quick to reply, I had barely poured a glass of wine lol). Something I need to remember in general, if it's {} [] or () it's empty, not redundant. Thanks for that. As for the Fat Arrow Syntax  that would explain why I was scouring through and couldn't find any mention or use of 'function'. I suddenly thought "ah, it doesn't always need a function, huh". So, I learned all about parseInt and parseFloat when I got started with Sheet Workers, the need to have a default numerical value too (which I believe is the 0 after the ||).  I can fully relate to what you mean there. I think you're right, I got confused at them being missing. What I am going to do tomorrow is strip all of this apart, put it back together in laymens terms and see if I can make it work again. I'll simplify elements and bits as you have later when I am closer toward your level of understanding. But for now, there is light at the end of the tunnel, I am beginning to understand more. Thanks again, you've been a real life-line for learning this and I couldn't credit you more for doing so.  EDIT: The irony that I don't need this on my sheet, but now that i've begun trying... it feels essential. Ha. *Rolls eyes* Humanity, eh?
1538169015
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Hehe, just passing it on. I learned from the real pros like Aaron, Jakob, Vince, and Chris. Pay it forward.