I've created a Custom Form Field Control for Angular 8 compatible with Reactive Forms and Angular Material. However, it being a simplified Rich-Text Editor, it has a header with various buttons with actions for the user.
How can I move the Placeholder label below the header of my input control to the actual textarea?
Current placeholder label placement
Well, since nobody really responded to my question, I'll post my own solution to this problem, one cruder (Solution 2) and one I deem proper (Solution 2, though to me it appears otherwise).
Edit: Solution 2: Proper approach
Well, I tried to make my code slightly more-configurable, hence I made one little mistake when changing code - I changed one variable to static, a variable used to declare the ID MatFormField uses in a class definition inside itself which we can use to customize the look of our component.
Namely controlType. Using this variable, we can identify when our component is in-use by direct class name following naming convention mat-form-field-type-<controlType>. So, since my controlType = "app-text-editor", I can use it like this:
.mat-form-field-type-app-text-editor:not(.mat-form-field-should-float) .mat-form-field-label-wrapper > .mat-form-field-label {
margin-top: 2.5rem;
padding: .5em;
}
Original: Solution 1: Hacky-approach
What I did was change my component to encapsulation: ViewEncapsulation.None, used the selector of my component inside css as my main identifier (in my case: app-text-editor) and then used CSS Sibling selector to select the floating label and placeholder to set offset for my TextEditor header and reset it back to default once the label is floating. The resulting CSS looks like this:
app-text-editor {
// Styling for actual TextEditor
&.floating ~ .mat-form-field-label-wrapper > .mat-form-field-label {
margin-top: initial;
padding: initial;
}
& ~ .mat-form-field-label-wrapper > .mat-form-field-label {
margin-top: 2.5rem;
padding: .5em; // Used to properly align the content inside my contenteditable
}
Or as pure CSS would look like:
app-text-editor.floating ~ .mat-form-field-label-wrapper > .mat-form-field-label {
margin-top: initial;
padding: initial;
}
app-text-editor ~ .mat-form-field-label-wrapper > .mat-form-field-label {
margin-top: 2.5rem;
padding: .5em; /* Used to properly align the content inside my contenteditable */
}
Weirdly enough, even the animation transition looks smooth despite me using such a hacky-approach for repositioning it.
If you don't mind using advanced CSS selector (Can I Use: Advanced CSS3 selectors), the solution can be even cleaner:
SCSS:
app-text-editor {
// Styling for the actual TextEditor
&:not(.floating) ~ .mat-form-field-label-wrapper > .mat-form-field-label {
margin-top: 2.5rem;
padding: .5em; // Used to properly align the content inside my contenteditable
}
Pure CSS:
app-text-editor:not(.floating) ~ .mat-form-field-label-wrapper > .mat-form-field-label {
margin-top: 2.5rem;
padding: .5em; /* Used to properly align the content inside my contenteditable */
}
Related
Edit - Following comments and thinking this through a bit, I have added the options as I see them to the bottom of this question.
Original Question
I have an Angular component (down below, html and scss file, nothing really in the component class for brevity). Lets call it "h5-underliner" and used like:
<app-h5-underliner>My Title</app-h5-underliner>
`
h5 {
margin-bottom: 1.8rem;
padding-bottom: 2rem;
position: relative;
}
h5:after {
content: "";
position: absolute;
bottom: 0;
left: 0.1rem;
height: 0.2rem;
width: 6rem;
background: $colour-apg-accent;
}
<h5 [class]="colourThemeName">
<ng-content></ng-content>
</h5>
`
Now say for example I want an "h2 underliner"
<app-h2-underliner>My Heading 2 Title</app-h2-underliner>
Which picks up the apps h2 styling.
How can I aceive this without duplicating all the css (which would end up something like the below:)
`
h2 {
margin-bottom: 1.8rem;
padding-bottom: 2rem;
position: relative;
}
h2:after {
content: "";
position: absolute;
bottom: 0;
left: 0.1rem;
height: 0.2rem;
width: 6rem;
background: $colour-apg-accent;
}
`
It's not a huge example, but I dont like the repetition. I thought about a directive, but the pseudo selector after kills that idea (as far as I am aware).
Note there is a little bit of extra going on as the caller can choose a colour, but that just means more repetition between very similar components (i.e. only the h5 and h2 tags differ).
So options:
Option 1 - The mess abouve with <h2-underliner>Mt Title Text<\h2-underliner> and <h5-underliner>Mt Title Text<\h5-underliner> - bad for several reaons.
Option 2 - Global style for the css that can be applied to tags as required (.my-underliner) OR we make the decision that all h5 and h2 titles have this style. On one hand we set typography globally with mat custom typography - but it doesn't seem right to start adding global css for general css.
Option 3 - A component that follows the same pattern as material form field/content projection: <app-underliner><h5>My title text</h5></app-underliner> - in this case we contain the common style to one component and still have the freedom to use it with different headings.
Styles follow hierarchical override in angular. So if both of those components that you refer lets call them B, C are children of component A, then you can put those styles on component A.
Then B, C will inherit those styles from parent A
They don't even have to be direct children of A to inherit styles of A.
So in a structure like
A - D - C
|_ B
D, C , B will inherit styles declared in component A
A script element that got styled as display:block appears visible. Why is it possible and is there any real use case where it is desired?
td > * {
display: block;
}
<table>
<tr>
<td>
<script type="text/javascript">
var test = 1;
</script>von 1
</td>
</tr>
</table>
The HTML5 specification defines a style sheet that user agents (like browsers) are expected to use. Section 10.3.1 lists the styles for "Hidden elements":
#namespace url(http://www.w3.org/1999/xhtml);
[hidden], area, base, basefont, datalist, head, link,
meta, noembed, noframes, param, rp, script, source, style, template, track, title {
display: none;
}
embed[hidden] { display: inline; height: 0; width: 0; }
As you can see, it applies display: none; to script.
This is the only "barrier" between your users and hidden script elements. It’s perfectly fine and intended to be able to overwrite styles from user-agent style sheets within author style sheets (and of course also within user style sheets).
Why someone might want to use it? One use case is displaying content without having to escape characters like </>, similar to the old xmp element. The script element can be used not only for scripts, but also for data blocks (i.e., for anything with a MIME type).
Why can <script> Tags be visible?
Because they are HTML elements like any other and there is no reason to write special case rules in the HTML specification (which would add complexity) to prevent CSS from applying to them.
Any element can be styled. Take, for example:
head { display: block; }
title { display: block; }
meta { display: block; }
meta[charset]:after { display: block; content: attr(charset); }
meta[content]:after { display: block; content: attr(content); }
Is there any Usecase where it is wanted?
Certainly no common ones, but general rules aren't designed to make sense for everything that you can apply them to. They are designed for the common cases.
Another (not common) use case:
I sometimes use <script> tags for brief HTML code examples in style guides. That way I don't have to escape HTML tags and special characters. And text editor tag autocomplete and syntax highlighting still work. But there's no easy way to add syntax highlighting in the browser.
script[type="text/example"] {
background-color: #33373c;
border: 1px solid #ccc;
color: #aed9ef;
display: block;
font-family: monospace;
overflow: auto;
padding: 2px 10px 16px;
white-space: pre-wrap;
word-break: break-all;
word-wrap: break-word;
}
<p>Here comes a code example:</p>
<script type="text/example">
<div class="cool-component">
Some code example
</div>
</script>
Possible use case: for debugging purposes.
You could apply a class at the document level, eg. <body class="debugscript">, then use some CSS:
body.debugscript script {
display: block;
background: #fcc;
border: 1px solid red;
padding: 2px;
}
body.debugscript script:before {
content: 'Script:';
display: block;
font-weight: bold;
}
body.debugscript script[src]:before {
content: 'Script: ' attr(src);
}
Script tags are hidden by default by using display:none;. Unor1 explains the underlying language specification. However, they are still part of the DOM and can be styled accordingly.
That said, it is important to keep in mind exactly what a script tag is doing. While it used to be accompanied by types and languages, that is no longer required. It is now assumed that JavaScript is in there, and as a result browsers will interpret and execute the script as it is encountered (or loaded) from these tags.
Once the script has been executed, the content of the tag is only text (often hidden) on the page. This text can be revealed, but it can also be removed because it is just text.
At the bottom of your page, right before the closing </html> tag, you could very easily remove these tags along with their text and there would be no changes to the page.
For example:
(function(){
var scripts = document.querySelectorAll("script");
for(var i = 0; i < scripts.length; i++){
scripts[i].parentNode.removeChild(scripts[i]);
}
})()
This will not remove any functionality, as the state of the page has already been altered and is reflected in the current global execution context. For example, if the page had loaded a library such as jQuery, removing the tags will not mean that jQuery is no longer exposed because it has already been added to the page's runtime environment. It is essentially only making the DOM inspection tool not show script elements, but it does highlight that the script elements once executed really are only text.
1. unor, Thu Jul 07 2016, wutzebaer, "When should tags be visible and why can they?", Jul 1 at 10:53, https://stackoverflow.com/a/38147398/1026459
I have a CSS file that controls all form elements with the following code:
input, textarea, select, .uneditable-input {
border: 1px solid #bbb;
width: 100%;
margin-bottom: 15px;
#include box_sizing;
}
I have one form though that I don't want the width: 100% element so I try setting up an ID for the input specifically as such
#analysis {
.input {
width: auto;
}
}
In my form which is written in Ruby on Rails I call a radio button as follows
<%= radio_button_tag :choice, "best_beer", true, class: "analysis" %>
I can inspect the element, it shows that it's using the analysis class, however the first CSS always takes precedents. I don't know much about CSS but I'm using the logic found here: http://www.htmldog.com/guides/css/intermediate/specificity/ Where a ID based CSS should take control over an HTML selector, why is it not doing so for me? Is it because my class is in Ruby?
You're mixing up the id and the class for the element and you shouldn't be nesting selectors since they both directly apply to the same element. You're using the #analysis selector in your CSS, which is for an element with the id of analysis, but in your embedded Ruby you're tagging the element with the analysis class. With the Ruby you're using, try this CSS:
input.analysis {
width: auto;
}
Two mistakes in your second SCSS or LESS file (3 if it is only a CSS file).
Should be input (for <input/>), not .input (for <ANY class="input"/>).
#analysis input selects all <input/> inside something with the id analysis, not <input id="analysis"/>.
This CSS rule should work:
input.analysis {
width: auto;
}
I styled an <input type="file"/> using CSS. When I click on a + button, it will be cloned. However this does only visually happen with an unstyled upload button.
Hint: In order to replace the standard button with a styled one, I set input[type="file"] { display:none }. Commenting this line out, the cloned upload buttons become visible, however without styles.
Is there a way to clone CSS styled buttons?
See Fiddle
You'll need to clone the label in addition to the input.
This clones the first label, while ensuring that it works with its own input:
$('label').first()
.clone(true)
.attr('for','img'+addcounter)
.insertBefore($('#add'));
Fiddle
Reconfigure your HTML, then clone the label
Form elements such as input can be children of label elements (w3.org, 17.9.1 The LABEL element), and doing so will make it easier to clone both with one statement.
Below, I do this and then assign the id attribute to the parent label for easier targeting.
<label id="img1" class="uploadbutton">Choose File
<input type="file" name="img1"/>
</label>
Note: You could leave the id attribute on the input and simply use jQuery's .parent() method to get the label if you prefer. There is more than one way to paint a fence.
The script then clones the label and its children in one statement. Notice the addition of .find(input) to set the attributes on the child input.
Example:
var addcounter = 2;
$("#add").on('click', function (e) {
//Create a new select box
$('#img1')
.clone()
.attr({
id: "img" + addcounter
})
.insertBefore($('#add'))
.find("input")
.attr({
name: "img" + addcounter
});
addcounter++;
});
td {
width: 100px;
}
input[type='file'] {
display: none;
}
#img2 {
color: red;
}
#img3 {
color: blue;
}
.uploadbutton {
margin-right: 25px;
padding: 6px 15px 6px 15px;
cursor: default;
color: #000;
font-size: 15px;
text-align: center;
border: 1px solid #000;
border-radius: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<label id="img1" class="uploadbutton">Choose File
<input type="file" name="img1"/>
</label>
<button id="add">+</button>
Update:
There is an additional benefit to nesting input elements within label elements, which is that you can freely position the parent label while the child input inherits that positioning by default.
The input then can be relatively or absolutely positioned within it, which is easier than trying to manage the position of two independent siblings and better than applying an unnecessary container element to achieve the same effect.
You don't need to make use of that benefit in this example, but I felt it was worth stating for good measure.
You are not styling the input at all, you are styling the label, and then only cloning the input. Try also cloning the label.
I think you might want to adapt this for type="file"
I need to create an HTML text input element that features multicolored placeholder text. All of the text should be gray except, but a closing asterisk should be red, as in:
This strikes me as a seemingly simple task that is actually a lot more complicated because of how browsers restrict our ability to style native input elements.
I have heard of people using CSS to override native input styles so they can use custom fonts, etc., but is there away to have two special text styles (gray and red)? Or do I need to use an alternative (non-native) input?
Try something like this: http://jsfiddle.net/vmuJm/
The trick: address the placeholder text, add a "required" class to required inputs, and use the :after pseudo element to add an appropriately colored asterisk.
[EDIT] It looks like this is only working for Webkit browsers.
I have a rather fun way to do this and seems to work great in all browsers.
(Works fine in IE 8+, chrome, and Firefox.)
What I am doing is using the spans I put inside of the label to act as the value text.
Here is the html structure,
<label><span class="title">Name<span class="symbol">*</span></span>
<input type="text" />
</label>
The css,
label {
position: relative;
}
label:hover span {
display: none;
}
input[type="text"]:focus, input[type="text"]:active {
z-index: 2;
}
label input[type="text"] {
position: relative;
}
.title {
color: gray;
position: absolute;
left: 5px;
top: 1px;
z-index: 1;
}
.symbol {
color: red;
}
Last here is the jQuery I wrote to not allow the span to hover over your input if the input is filled in.
$('input[type="text"]').blur(function() {
if( $(this).val().length >= 1) {
$(this).toggleClass('active');
}
else {
$(this).removeClass('active');
}
});
Here is a JSFIDDLE to play with.