dynamic stylesheet with angularjs - css

I have and angularjs application that fetches data via api, and builds a webpage with it.
Usually I use ng-style to create dynamic styling, but now I have to use the nth-of-type attribute that can only be used in a css stylesheet (I cannot use individual styling since the number and order of elements always change).
I have tried this naive code (in the html page):
<style ng-if="styles.sc && styles.sc.length==3">
a.mosection:nth-of-type(3n) > div {
background-color: {{styles.sc[0]}} !important;
}
a.mosection:nth-of-type(3n+1) > div {
background-color: {{styles.sc[1]}} !important;
}
a.mosection:nth-of-type(3n+2) > div {
background-color: {{styles.sc[2]}} !important;
}
</style>
But it didn't work... Apparently angular doesn't bind the data inside the style tag (the ng-if attribute does get digested properly)
Does anyone have any idea how this can be done?
Thanks!

You should checkout those three ng-*
https://docs.angularjs.org/api/ng/directive/ngClass
https://docs.angularjs.org/api/ng/directive/ngClassOdd
https://docs.angularjs.org/api/ng/directive/ngClassEven
all of them can accept functions as attributes, you can also checkout
https://docs.angularjs.org/api/ng/directive/ngStyle
which might be actually the best in your case

Thanks!
I indeed solved it by using ng-style with a function
The HTML
<div class="widget widget-people" ng-style="{backgroundColor: staggerBgColors('widget', 'widget-people', '#333333')}"></div>
<div class="widget widget-property" ng-style="{backgroundColor: staggerBgColors('widget', 'widget-property', '#24d10f')}"></div>
The scope function
$scope.staggerBgColors = function(elesClass, eleClass, defaultColor){
if (!$scope.styles || !$scope.styles.sc || $scope.styles.sc.length!=3){
return defaultColor;
}else{
var listItem = $('.'+eleClass);
var n = $('.'+elesClass).index( listItem ) % 3;
return '#' + $scope.preview.moment.sc[n];
}
}
I had to implement the same functionality of the css property "nth-of-type" using jQuery, but it works prefectly!

Related

How to think about styling AngularJS components?

I'm working on an AngularJS project with the aim of slowly getting things in order for Angular 6, or whatever version is out when we start on the upgrade. One of the big pieces of that work is converting existing directives into components.
The thing I'm struggling the most with, is that every instance of a component introduces an extra element into the DOM that wraps my actual component HTML and breaks the hierarchy, making it very hard to write CSS that does what it needs to.
To illustrate my dilemma, imagine a simple component called alert that provides styling for various types of messages you want a user to pay attention to. It accepts two bindings, a message and a type. Depending on the type we will add some special styling, and maybe display a different icon. All of the display logic should be encapsulated within the component, so the person using it just has to make sure they are passing the data correctly and it will work.
<alert message="someCtrl.someVal" type="someCtrl.someVal"></alert>
Option A: put styling on a <div> inside the extra element
Component template
<div
class="alert"
ng-class="{'alert--success': alert.type === 'success', 'alert--error': alert.type === 'error'}">
<div class="alert__message">{{alert.message}}</div>
<a class="alert__close" ng-click="alert.close()">
</div>
Sass
.alert {
& + & {
margin-top: 1rem; // this will be ignored
}
&--success {
background-color: green; // this will work
}
&--error {
background-color: red; // this will work
}
}
This works fine as long as the component is completely ignorant of everything around it, but the second you want to put it inside a flex-parent, or use a selector like "+", it breaks.
Option B: try to style the extra element directly
Component template
<div class="alert__message">{{alert.message}}</div>
<a class="alert__close" ng-click="alert.close()">
Sass
alert {
& + & {
margin-top: 1rem; // this will work now
}
.alert--success {
background-color: green; // nowhere to put this
}
.alert--error {
background-color: red; // nowhere to put this
}
}
Now I have the opposite problem, because I have nowhere to attach my modifier classes for the success and error states.
Am I missing something here? What's the best way to handle the presence of this additional element which sits above the scope of the component itself?
I personally do option A. This allows you to easily identify and create specific styles for your components without fear that they will overwrite site-wide styles. For instance, I'll use nested styles to accomplish this:
#componentContainer {
input[type=text] {
background-color: red;
}
}
This will allow you to make generic styles for your component that won't spill out into the rest of your solution.

User Inputted live Color Scheme Change

I designed a site so that changing two user inputted colors should change the color scheme of the entire site.
What is the best way to accomplish this. I know that I would have to save the items in the database and pull every time the user logged in in order to implement the color scheme with every login.
But at the moment I am more worried about a live change as soon as the user changes the html color value.
I know of an option to where I add a CSS class to every component that would change such as ... .primaryColor and .secondaryColor. And then alter all of the elements with that class. Is there a better way with React or another CSS/Javascript solution?
Also another complication is that it would have to be in a way that when the user loads other components that have not rendered yet, the change is still in affect.
One possible solution is to use the <style> element coupled with dangerouslySetInnerHTML, like this. (Notice the backticks ` around the CSS - it's an interpolated string literal.)
const Theme = props => {
<style dangerouslySetInnerHTML={{__html: `
.styled { color: ${props.userColor} }
`}}
/>
}
Then a component that used the theme would simply be <div className="styled" />
I got the idea for this solution here.
If you use this method, be very careful you're using sanitized variables to create your CSS theme. Otherwise, there's potential problems with injection attacks.
I would use an event listener on the input, read the value, and if it matches whatever you want to trigger the color scheme change, apply the theme value to a data attribute on a root element and use CSS to control the color schemes.
var input = document.getElementById('input'),
body = document.getElementsByTagName('body')[0];
input.addEventListener('keyup',function() {
var val = this.value;
if (this.value == 'foo') {
body.setAttribute('data-theme','secondary');
} else if (this.value == 'bar') {
body.setAttribute('data-theme','primary');
} else {
body.setAttribute('data-theme','');
}
// ajax request to save theme pref in db
})
/* defaults */
body {
color: #333;
}
/* primary theme */
[data-theme="primary"] {
color: red;
}
[data-theme="primary"] p {
background: yellow;
}
/* secondary theme */
[data-theme="secondary"] {
color: blue;
}
[data-theme="secondary"] ul {
background: grey;
}
<input id="input" placeholder="enter 'foo' or 'bar'">
<p>
paragraph
</p>
<ul>
<li>list</li>
</ul>
you can easily do this using js.
just add your class .primaryColor, .secondaryColor with jQuery addClass() Method.
select the element
example :
$(selector).addClass(classname,function(index,currentclass))
more example :https://www.w3schools.com/jquery/tryit.asp?filename=tryjquery_html_addclass

Hide a whole div with CSS with part of it is empty

Is there a way to hide a whole div if part of it is empty? For example if "dd" is empty as shown below can I hide the whole class "test" so the keyword Restrictions does not show either. I tried .test dd:empty { display: none; } but this does not work. thanks!
<div class="test"><dt>Restrictions:</dt>
<dd></dd></div>
I don't think there's any easy way to do what you're talking about with just CSS. Better to test it server-side if you can. But if you can't here's some JS that will do the job.
<script type="text/javascript">
// handles multiple dt/dd pairs per div and hides them each conditionally
function hideIfEmpty() {
// get all the elements with class test
var els = document.getElementsByTagName('dl');
// for every 'test' div we find, go through and hide the appropriate elements
Array.prototype.map.call(els, function(el) {
var children = el.childNodes;
var ddEmpty = false;
for(var i = children.length - 1; i >= 0; i--) {
if(children[i].tagName === 'DD' && !children[i].innerHTML.trim()) {
ddEmpty = true;
} else if(children[i].tagName === 'DT') {
if(ddEmpty) {
children[i].style.display = 'none';
}
// reset the flag
ddEmpty = false;
}
}
});
}
window.addEventListener('load', hideIfEmpty);
</script>
<div class="test">
<div style="clear: both;"></div>
<dl>
<dt>Restrictions:</dt>
<dd></dd>
<dt>Other Restrictions:</dt>
<dd>Since I have content, I won't be hidden.</dd>
</dl>
</div>
Just a fair warning: the code uses some functions that may not exist in older IE, such as Array.prototype.map, String.prototype.trim, and addEventListener. There are polyfills available for these and you could also write your own pretty easily (or just do it with a for loop instead).
CSS alone can't do that. Either, you need a javascript to retrieve empty elements and hide their parents, or your CMS applies special CSS classes if there's no content.
Put as an answer as requested by #Barett.
You could update your CSS to be
.test{
display: none;
color: transparent;
}
This would make the text transparent too, but display:none should hide it anyway.
To make the div with the id test ONLY show when the dd tag is EMPTY, and you can use jQuery, try the following JavaScript along with the CSS:
if($("dd").html().length ==0)
{show();
}
Note: this solution requires jQuery, which is a JavaScript library.

Is there a CSS "haschildren" selector? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is there a CSS parent selector?
Is there a css selector I can use only if a child element exists?
Consider:
<div> <ul> <li></li> </ul> </div>
I would like to apply display:none to div only if it doesn't have at least one child <li> element.
Any selector I can use do this?
Sort of, with :empty but it's limited.
Example: http://jsfiddle.net/Ky4dA/3/
Even text nodes will cause the parent to not be deemed empty, so a UL inside the DIV would keep the DIV from being matched.
<h1>Original</h1>
<div><ul><li>An item</li></ul></div>
<h1>No Children - Match</h1>
<div></div>
<h1>Has a Child - No Match</h1>
<div><ul></ul></div>
<h1>Has Text - No Match</h1>
<div>text</div>
DIV {
background-color: red;
height: 20px;
}
DIV:empty {
background-color: green;
}
Reference: http://www.w3.org/TR/selectors/#empty-pseudo
If you go the script route:
// pure JS solution
​var divs = document.getElementsByTagName("div");
for( var i = 0; i < divs.length; i++ ){
if( divs[i].childNodes.length == 0 ){ // or whatever condition makes sense
divs[i].style.display = "none";
}
}​
Of course, jQuery makes a task like this easier, but this one task isn't sufficient justification to include a whole libary.
Nope, unfortunately that's not possible with CSS selectors.
CSS does not (yet) have any parent rules unfortunately, the only way around it if you must apply it only parents that contain a specific child is with the Javascript, or more easily with a library of javascript called jQuery.
Javascript can be written in a similair way to CSS in someways, for your example we would do something like this at the bottom of our HTML page:
<script type="text/javascript">
$('div:has(ul li)').css("color","red");
</script>
(For this you would need to include the jQuery library in your document, simply by putting the following in your <head></head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
If you use jquery, you can try out this function
jQuery.fn.not_exists = function(){
return this.length <= 0;
}
if ($("div#ID > li").not_exists()) {
// Do something
}
There is another option
$('div ul').each(function(x,r) {
if ($(r).find('li').length < 1){
$(r).css('display','block'); // set display none
}
})

CSS if/else statement for counting list items

I need an if/else statement for my CSS which can count list items. Would this be possible?
Basically I want to say, if there are less than 10 list items, the UL container should be 200px wide, and it there are more than 10 list items, it should be 400px wide. Something like that.
Can it be done?
I would appreciate a working demo on jsFiddle, both so I can see working code, and for anyone who looks here in the future so they can see a working example and how to do it :)
CSS only does styles, but not dynamically (unless with assistance of JS). you can use the following JS snippet for the task. just to make sure, load this at the very last, just before the </body>
<script type="text/javascript">
(function resize() {
//get all lists with selected name
var lists = document.getElementsByClassName('myList');
//loop through all gathered lists
for (i = 0; i < lists.length; i++) {
//shorthand elements for easy use
var list = lists[i];
var items = list.getElementsByTagName('li');
//append class names
list.className = (items.length < 10) ? 'myList less' : 'myList more';
}
}())​
</script>
.less{
width:200px;
}
.more{
width:400px;
}​
CSS has no if else statements. You can do this easily with jQuery. Another option would be to use LESS or SCSS.
Short answer: no. CSS offers no conditional support.
Long answer: you need to use javascript or a server side language to either add a class when there are more than 10 items (or elements) in the list, or in the case of javascript, directly manipulate the style after it's loaded.
That doesn't sound possible for CSS. There are no logical if/else statements in the CSS spec. Your next best bet would probably be javascript. You could achieve this with jQuery with the following code:
if($('ul#target-list li').length < 10) {
$('ul#target-list').css('width', 200);
}
else {
$('ul#target-list').css('width', 400);
}
Pure CSS3 Solution
If you only want to support CSS3, then this does what you need:
li {
width: 200px;
}
li:nth-last-child(n+11),
li:nth-last-child(n+11) ~ li {
width: 400px;
}
But you will need to make the ul either display: inline-block or float it so that the width is controlled by the li elements themselves. This may require you to wrap the ul (display: inline-block) in a div so that it still is a block element in the flow of the page if you need it so.

Resources