Proper way to author Jquery plugins with style rules - javascript

I have been long battling this, and I would like to know if any others have feedback. I am about to make a customized library for building web apps quickly, and I want to make sure I use the right approach. I WANT to use this method:
$.fn.someSlider = function(){
var coreStyle = '.slider ul { white-space: nowrap; } .slider ul li {display: inline-block}', coreStyleTemplate = '<style><\/style>';
}
But I feel like hard coding the base CSS into the widget is always frowned upon - instead I see SO users recommending the use of CSS style rules instead of this option. I really really really want that 'it just works' feel, and having to force my users to use a separate style sheet just to get my plugins working... well is annoying!
Just to clarify: I would like to include all base style rules needed for the widgets proper/base functionality to be included inside the script. The user would easily modify the base look of the widget by writing a style rule in their own style sheet.
Example:
Instead of having to look through all the base styles trying to find the font color like this... .slider {display: inline-block; color: #000; someotherconfusingrule : blahblah; }
The user simply starts a new rule with the classes name/selector being used - and then just write the changes to make to the default script styles
They would just write
.slider {color: #000};
Thanks for the help in advance SO!

Nice question! Although I'm not sure what the preferred solution to this would be, I was thinking of the following approach:
Use a IIFE to define your jQuery plugin and enable you to define some private, global variables and functions.
$.fn.pluginName = (function() {
return function() {
...your regular plugins code...
};
}();
Define your plugins CSS as a list of style rules in your plugins code
var rules = [
'.box {' +
' width: 100px;' +
' background-color: #f99;' +
' margin: 10px;' +
' padding: 10px;' +
' font-family: Helvetica, Arial;' +
' text-align: center;' +
'}'
];
Create a private variable that remembers if your stylesheet has already been added to the document
var styleSheetExists = false;
Create a private function that creates a stylesheet using the style rules above and that adds it as the first <style> element in the <head> allowing the user to override styles in their own CSS. See http://davidwalsh.name/add-rules-stylesheets for a good tutorial on how to do this properly
var createStyleSheet = function() {
var style = document.createElement("style");
style.appendChild(document.createTextNode(""));
$('head').prepend(style);
for (var i = 0; i < rules.length; i++) {
style.sheet.insertRule(rules[i], i);
}
};
The first time your plugin is applied to an element check if the stylesheet has already been created and if not create the stylesheet.
var $elements = $(this);
if (!styleSheetExists) {
createStyleSheet();
styleSheetExists = true;
}
$elements.each(function() {
$(this).addClass('box');
});
return $elements;
See http://codepen.io/ckuijjer/pen/FkgsJ for this example. It creates a jQuery plugin called box which simply adds the class box to an element. The class box has a default pink background color defined in its stylesheet which gets overridden by a user defined blue background color.
But please do make this configurable in your jQuery plugin. You want to enable developers to bundle all their css, including your plugins, to optimize resource delivery to the client. Plus injecting stylesheets might be a small performance hit.

It may seem annoying but separating the model, view, and controller is the correct way. You're using jQuery so why not consider how jQuery would approach the situation: a jQuery UI widget like the Accordion comes with several stylesheets, the most important being the base stylesheet and a separate 'theme' stylesheet that (if done correctly) is nondestructive and can be modified without risking the integrity of the widget. You may also want to consider how your favorite plugins are authored and what makes them appeal to you. It's my personal opinion CSS should never be present in JavaScript files however if you've made up your mind, the solution #ckuijjer provided is sound. Hope this helps!

Related

Getting a field from a named CSS style in Javascript

I have an HTML document with a link tag in its head to a particular CSS stylesheet:
<link rel="stylesheet" href="style.css" type="text/css">
This .css file contains a particular class, like so:
.mystyle {
color: #00c;
}
What I'm trying to do is to grab that class's color field, so that I can use it dynamically in another part of the page (for another element's background-color). Is there any way in a JavaScript program to access that information, by the name of the class? Something like this:
var myColor = document.getStyle(".mystyle").color;
Some caveats:
There may or may not be other stylesheets that are also linked from this HTML document.
There may or may not be any particular elements on the page that are styled with this particular class.
I've already tried setting a temporary element to have the given class, and then grabbing its color field. That didn't work: the color field contains the empty string.
Thanks.
You can get all stylesheet information using the StyleSheetList and related objects.
In the example below, I aggregate all the document's styles (i.e., inline styles, an external bootstrap stylesheet and the stylesheet provided by Stackoverflow), and retrieve the color information for the .mystyle class:
const sheets = [...document.styleSheets];
const rules = sheets.reduce((a, v) => [...a, ...v.cssRules || []], []);
const rule = rules.find(r => r.selectorText === '.mystyle');
console.log(rule.style.color);
.mystyle {
color: #00c;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"/>
It's possible to use JavaScript to read the actual CSS files themselves by scraping the DOM and extracting the relevant information. While possible, it's clunky, and I'd advise against that unless absolutely necessary. If it's required, this answer covers it pretty well.
As an alternative to scraping the header information, you could use HTMLElement.style and grab the color value, though note that this will only work for inline styles:
var span1 = document.getElementsByTagName('span')[0];
var span2 = document.getElementsByTagName('span')[1];
// Empty
console.log(span1.style.color);
// Blue
console.log(span2.style.color);
.mystyle {
color: #00c;
}
<span class="mystyle">Text</span>
<span style="color: #00c;">Text</span>
However, a much better solution would be making use of what are known as CSS variables. These are defined in :root with a double hyphen prefix, and can be referenced with var(). This allows you to only set a colour once, and re-use it for both a color property and a background-color property, as can be seen in the following:
:root {
--colour: #00c;
}
.a {
color: var(--colour);
}
.b {
background-color: var(--colour);
}
<span class="a">Text</span>
<span class="b">Text</span>
Hope this helps! :)
Try window.getComputedStyle in combination with getPropertyValue.
var elem = document.getElementsByClassName("mystyle");
var theCSSprop = window.getComputedStyle(elem,null).getPropertyValue("color");
More: https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle
For any who might come after me:
One can indeed use window.getComputedStyle(element) on an element. However, creating your own element first (if one doesn't exist) comes with an important caveat. Firefox will properly calculate the computed style. However, Chrome (and possibly Safari too) won't calculate the style of an orphaned element that isn't part of the DOM tree. So if you go that route, be sure to add it to the tree somewhere, possibly as a hidden element.

Shadow DOM styling from the outside

I am searching a way to styling shadow DOM from the outside. For example, I would like to set the color of all text in all 'span.special' elements as RED. Including 'span.special' elements from shadow DOM. How I can do this?
Previously there were ::shadow pseudo-element and /deep/ combinator aka >>> for this purpose. So I could write something like
span.special, *::shadow span.special {
color: red
}
But now ::shadow, /deep/ and >>> are deprecated. So, what do we have as a replacement of them?
I did try many methods, including those described here. Since I'm using an external Web Component lib, I don't have access to modify these components. So, the only solution that worked for me was using JS querySelector, like this:
document.querySelector("the-element.with-shadow-dom")
.shadowRoot.querySelector(".some-selector").setAttribute("style", "color: black");
Not the best solution, not suitable for large stylings, but does work for little enchancements.
#John this was tested with Chrome 83.0.4103.116 (still going to test in Safari) and I did for Ionic (v5) ion-toast component. Here is the (almost) real code I used:
import { toastController } from '#ionic/core';
let toastOpts = {
message: "Some message goes here.",
cssClass: "toast-with-vertical-buttons",
buttons: [
{
text: "Button 1",
side: 'end'
},
{
text: "Button2",
side: 'end'
},
{
icon: "close",
side: "start"
}
]
}
toastController.create(toastOpts).then(async p => {
let toast = await p.present(); // this renders ion-toast component and returns HTMLIonToastElement
toast.shadowRoot.querySelector('div.toast-button-group-end').setAttribute("style", "flex-direction: column");
});
There is still no easy way to pierce through the shadow root, but here are 3 ways you can go about it. Just keep in mind that you will need to make changes inside the web component.
Using variables v1 - You will need to pass the property and consume the variable inside the web component.
Using variables v2 - You will need to consume the variable inside the web component.
Using ::part() - You will need to add a part attribute to the element you want to style in the web component. (Note: this pseudo element is well supported but is still in experimental mode, so make sure you're aware of that before using it in production).
Run code sample below for details.
const elA = document.querySelector('custom-container-a');
const shadowRootA = elA.attachShadow({mode:'open'});
shadowRootA.innerHTML = '<style>:host([border]) {display:block;border: var(--custom-border);}</style>'+
'<p>Shadow content A</p>'
const elB = document.querySelector('custom-container-b');
const shadowRootB = elB.attachShadow({mode:'open'});
shadowRootB.innerHTML = '<style>p {display:block;color: var(--custom-color, blue);}</style>'+
'<p>Shadow content B</p>'
const elC = document.querySelector('custom-container-c');
const shadowRootC = elC.attachShadow({mode:'open'});
shadowRootC.innerHTML = '<p part="paragraph">Shadow content C</p>'
/* Normal way of styling */
p {
color: orange;
}
/* Using variables version 1 */
custom-container-a {
--custom-border: 3px solid gold;
}
/* Using variables version 2 */
custom-container-b {
--custom-color: green;
}
/* Using ::part() */
custom-container-c::part(paragraph) {
color: magenta;
}
<p>Light content</p>
<custom-container-a border></custom-container-a>
<custom-container-b></custom-container-b>
<custom-container-c></custom-container-c>
You could use #import css as explained in this answer to another question on SO.
Include the rule inside the style element in the shadow tree.
<style>
#import url( '/css/external-styles.css' )
</style>
Note that the >>> combinator is still part of the CSS Scoping Module Draft.
Well, #import is not a solution if you are working with library web component that you can't change ...
Finally I found several ways to do it:
1) Cascading. Styles of Shadow DOM's host element affect Shadow DOM elements also. Not an option if you need to style a particular element of the Shadow DOM, not every.
2) Custom properties https://www.polymer-project.org/1.0/docs/devguide/styling
If an author of the web component provided such.
3) In Polymer, the have Custom Mixins also https://www.polymer-project.org/1.0/docs/devguide/styling
4) #import, but only for not-library components
So, there are several possibilities, but all of them are limited. No powerful enough way to outside styling as ::shadow were.

innerHTML added text to div , but content still invisible

I am loading the below function in a empty HTML document ::
setTimeout(function() {
(function test(){
var elem = document.createElement('div');
body = document.body;
body.appendChild(elem);
var
rule = "lalalalallalaallllllllllllllaaaaaaaaaaaaaaa" ,
mod = 'Alex-z',
style = ['­','<style id="s', mod, '">', rule, '</style>'].join('');
console.log(style);
elem.innerHTML += style;
})();
}, 2500);
Now I have a question, no matter how big rule is, I never see any text in the browser, why ? Can somebody explain, a very similar snippet is used in a JS feature detection library, called modenizer, so I would really be interested in knowing why nothing is showing up in my browser?
Because <style></style> is by default hidden in the browser and it is set display:none if you inspect element. It's because you wouldn't want your declared styles being showed in the browsers right?
I really don't know what you're going to do, but if you want to see your styles generated on that JS then use :
div style {
display: block;
}
You don't see any text in the browser because your output is:
"­
<style id="sAlex-z">lalalalallalaallllllllllllllaaaaaaaaaaaaaaa</style>"
­ is the soft hyphen symbol and will be converted into the HTML name, which is ­ and the result is not visible (http://www.ascii.cl/htmlcodes.htm)
The rest is just a <style> element, used only to set CSS rules and by default browser set style { display: none; }, so it's not visible.
a very similar sinppet is used in a JS feature detection library
called Modernizr
Yes, Modernizr use this kind of snippets to detect some features, because in most cases needs to create an empty element and try to set the property we are trying to test. Eg:
tests['textshadow'] = function() {
return document.createElement('div').style.textShadow === '';
}

Manipulate highlighted text with Javascript

HI,
I'm trying to develop some code in Javascript that adds highlighted text to a class. What I want to achieve with this is the ability of text highlighting with a custom color.
I want it to kind of look like the following:
window.getSelected = "<span class=\"highlighted\">" + window.getSelected + "</span>"
after the above code is executed the selected text's background is surrounded by span tags.
thanks,
fbr
You'll want to look into Range Objects, there is a good summary here:
http://www.quirksmode.org/dom/range_intro.html
Browser compatibility will be an issue, but basically, you can get the current selection in the way you suggest, then convert it into a Range, and use the methods of the Range object to find and split the existing DOM nodes, and insert your own <span> tag containing the selected text.
It's not entirely trivial, and involves getting into serious DOM manipulation, but it's a rewarding subject to get your head around. Enjoy!
If you are talking about styling native selections, use ::selection and ::-moz-selection in CSS.
::selection {
color: ...;
background-color: ...;
}
::-moz-selection {
color: ...;
background-color: ...;
}
Alternatively, if you want to highlight an arbitrary element with a class:
CSS
.highlighted {
color: ...;
background-color: ...;
}
JavaScript
yourElement.className = "highlighted";
excuse my english, do you mean adding a class to a text?
function changeClass (elementID, newClass) {
var element = document.getElementById(elementID);
element.setAttribute("class", newClass); // This is for good browsers
element.setAttribute("className", newClass); //For IE<
}
leave all lines, its harmless if you do that way.
If you are using the jQuery framework you can try with the following code:
var your_color = 'yellow';
$('.your-class').css('background-color', your_color);
If you're not using it I highly suggest you start; it makes things a lot easier, it's very stable and it's used by many popular websites including google and stack overflow itself.

How to get all css's settings using jquery?

I'd like to know all css settings in page using jquery. I know to get some css's setting, we do like this,
$('#container').css("width")
As this, I tried to use $('*') to get all css settings but couldn't success.
Please give me some advice.
If you want the elements with inline CSS try:
$("[style]").each(function() {
for (var i=0; i<this.style.length; i++) {
console.log(this.style[i] + " = " + this.style.getPropertyValue(this.style[i]));
}
});
Note: this uses the Javascript style names which are as per the CSS spec rather than those used in jQuery's css() (eg "margin-top" in CSS, "marginTop" in css()).
If you want all the style that's applied to an element based on its inline style and CSS rules defined both internally on the Web page and through external style sheets, that's going to be somewhat more difficult.
You can at least find the global style sheets with something like:
for (var i=0; i<document.styleSheets.length; i++) {
var css = document.styleSheets[i];
for (var j=0; j<css.length; j++) {
console.log(css[j] + " = " + css.getPropertyValue(css[j]));
}
}
For me your question isn't clear enough to answer. What is it exactly that you want to achieve?
all css settings in page
could mean a lot of different things.
Do you want the calculated CSS properties of the browser
Do you want the original CSS classes which apply (not the calculated values but the declared values from .css files) + all of them or only the most specific match
Do you want the original inline CSS stylings
Do you want a list of all included stylesheets
Do you really want all properties or only a selection thereof
Do you want this for every element on the page or only for specific ones
....

Categories