Duplicate a CSS rule using JavaScript only - javascript

I have a list that is generated elsewhere and I cannot change that.
The indent level is via & nbsp;, however the font-size is the same for each indent and I would like to change the font-size based on the indent level.
Therefore, I need to duplicate a css rule and change the new id and the font-size.
The following is the HTM generated code, which I cannot change:
<style type="text/css">
span.text12Font1 {
font-size:14px;
font-family:"Arial", sans-serif;
color:#010101;
font-weight:normal;
font-style:normal;
text-decoration:normal;
}
</style>
<div id="text12">
</a>
<ul style="margin-left:4px;text-align:left;" >
<li>
<span class="text12Font1">Emphasize the beginning of the bullet point</span>
</li>
<li>
<span class="text12Font1"> </span >
<span class="text12Font1">As in this list, when the first few words capture the main idea</span >
</li>
<li>
<span class="text12Font1"> That way, readers can skim easily</span>
</li>
</ul>
</div>
I can get each point and I can find all of the class names in each point.
What I need is the ability to duplicate a css class, give it a new id and just change the font-size.
I have the following so far:
function getNewClassName(className, newName, fSize){
var spanID = 'span.' + className;
//e.g.: span.text12Font1
for(var i=0; i<document.styleSheets.length; i++){
var sheet = document.styleSheets[i];
var cssText = sheet.ownerNode.innerText;
var posn = cssText.indexOf(spanID);
if(posn!=-1){
var oSheet = document.styleSheets[i];
var oRules = oSheet.cssRules ? oSheet.cssRules : oSheet.rules;
for(var r=0; r<oRules.length; r++){
if(oRules[r].cssText.indexOf(spanID)!=-1){
// Here I have the rule that I want to duplicate, change it's name to newName and change the font-size.
// I must not change the existing rule and it must remain as it could be used elsewhere
return true;
}
}
}
}
return false;
}

See update at the bottom of this post for a reference to editing CSS through JavaScript
Can you add a CSS class with Javascript to the containing li items.
You could count the number of occurences in each li and give the li a CSS class accordingly.
Something like this: http://jsfiddle.net/ncdajzur/
CSS
span.text12Font1 {
font-size:14px;
font-family:"Arial", sans-serif;
color:#010101;
font-weight:normal;
font-style:normal;
text-decoration:normal;
}
.whitespace1 span.text12Font1 {
font-size: 12px;
}
.whitespace2 span.text12Font1 {
font-size: 8px;
}
JavaScript (I used jQuery for quick testing purposes)
function formatText(id) {
var $list = $('#' + id);
$list.find('li').each(function(i) {
var numWhitespaces = ($(this).html().match(/ /g) || []).length;
$(this).addClass('whitespace' + numWhitespaces);
});
}
formatText('text12');
Update
An extensive explanation of how to manipulate stylesheets through JavaScript is available here:
http://www.hunlock.com/blogs/Totally_Pwn_CSS_with_Javascript#quickIDX1

AFAIK, you cannot duplicate a CSS rule with javascript. You can only apply a certain style on an element, but that will be applied within an inline style. So you can either change it by applying to the inline style or try something else.
I only know jquery way of doing that, that's by using $('.className').css('font', 'somefont')

I now have the following:
function duplicate_cssRule(passClassID, newClassID, fSize){
var nClassID = 'span.' + newClassID;
if(findCSS_Rule(nClassID)) return true; // Must have already done this one
var classID = 'span.' + passClassID.split(' ')[0]; // Might have multiple classes, we always want the first
var ruleObj = findCSS_Rule(classID)
if(!ruleObj) return false;
var cssText = ruleObj.cssText ? ruleObj.cssText : ruleObj.style.cssText;
var posn = cssText.indexOf('{');
cssText = cssText.substr(posn+1).trim().split(';');
for(var i=0; i<cssText.length; i++){
var fontData = cssText[i].toLowerCase().trim(); // IE is uppercase
if(fontData.substr(0,10)=='font-size:'){
cssText[i] = 'font-size:' + fSize + 'px';
break;
}
}
cssText = cssText.join(';');
cssText = cssText.substr(0,cssText.length-1);
if( styleSheet.insertRule ){
cssText = nClassID + ' {' + cssText + '}';
styleSheet.insertRule(cssText,styleSheet.cssRules.length);
}else if(styleSheet.addRule){
styleSheet.addRule(nClassID,cssText);
}
return true;
}
var styleSheet;
function findCSS_Rule(classID){
var sheets = document.styleSheets;
for(var i=0; i<sheets.length; i++){
styleSheet = sheets[i];
var styleRules = styleSheet.cssRules ? styleSheet.cssRules : styleSheet.rules;
if(styleRules){
for(var r=0; r<styleRules.length; r++){
if(styleRules[r].selectorText&&styleRules[r].selectorText.toLowerCase()==classID.toLowerCase()) return styleRules[r];
}
}
}
return false;
}
Works in all browsers, even IE8.

Related

Break CSS into rules

I have the content of a CSS file as a string I got with an AJAX request.
I need to break it into rules that I can feed into styleSheet.insertRule
What is the best way to achieve this correctly and efficiently ? I do not want to include/write a full CSS parser just for that if possible ...
I considered simply splitting on '}' but this will not work if there are #media clauses which created nested curly brackets, and I am not sure it is correct otherwise ...
Thanks!
You can simply create a new <style> element using the stylesheet text you have and insert it into the <head> element.
var style = `
div {
color: blue;
}`;
setTimeout(function() {
var elem = document.createElement("style");
elem.innerHTML = style;
document.head.appendChild(elem);
}, 1000);
<div>
Some text
</div>
The solution from Nisarg Shah probably does the job, but here's an alternative. It uses the css.js library to parse the CSS string into a JS array, which can then be inserted using stylesheet.insertRule().
The two advantages this method has:
It breaks the CSS into rules as stated in the question
It would allow styles to be filtered or altered before insertion
Also, this is a simplified solution that will not work with more complicated CSS containing media queries or similar rules. For a more robust solution, the function would need to handle those types of style rules, and more info on how to do that can be found in the MDN docs.
Here's the approach:
Get the CSS from your API
Parse the CSS into a JS array of objects
Loop through the CSS rules and create the CSS style string
Add the style string to the stylesheet with insertRule()
var cssString = `
div {
color: green;
font-size: 24px;
}`;
setTimeout(function() {
var parsedStyles = addStylesAndReturnParsedStyles(cssString);
console.log(parsedStyles)
}, 1000);
var addStylesAndReturnParsedStyles = function(cssString) {
var parser = new cssjs();
var parsed = parser.parseCSS(cssString);
var styleEl = document.createElement('style'),
styleSheet;
// Append style element to head
document.head.appendChild(styleEl);
// Grab style sheet
styleSheet = styleEl.sheet;
parsed.forEach(style => {
var selector = style.selector;
var rules = style.rules;
var propStr = '';
for (var i = 0; i < rules.length; i++) {
var prop = rules[i];
propStr += prop.directive + ':' + prop.value + ';\n';
}
// Insert CSS Rules
styleSheet.insertRule(selector + '{' + propStr + '}', styleSheet.cssRules.length);
});
return parsed;
};
<script src="https://cdn.rawgit.com/jotform/css.js/master/css.min.js"></script>
<div>Here's some content</div>

Nesting dynamic links into a dynamic list with Javascript

I have tried to create a function that creates a dynamic menu. Ive been able to create the "a" tags and give them individual links while also assigning them ID's. The problem is that I cant get the links inside of a list where my CSS applies its rules.
function write_navBar() {
var links = ["intro.html", "art.html", "portfolio.html", "guides.html", "about_me.html"]
var ul = document.createElement("ul");
document.getElementById("mainNav").appendChild(ul);
for (i = 0 ; i < links.length ; i++) {
var ul = document.createElement("ul");
var a = document.createElement("a");
var text = document.createTextNode(links[i]);
a.href = links[i];
a.id = "mainNav";
a.text = links[i];
document.getElementById("mainNav").appendChild(a);
}
}
Any suggestions on cleaning the code while keeping to javascript would be appreciated. And also any explanation of syntax would be also appreciated.
Thank you!
function write_navBar() {
var links = ["intro.html", "art.html", "portfolio.html", "guides.html", "about_me.html"];
var ul = document.createElement("ul");
var li, a, text;
for (var i = 0, l = links.length; i < l; ++i) {
li = document.createElement('li');
a = document.createElement('a');
text = document.createTextNode(links[i]);
a.href = links[i];
a.setAttribute("class", "some-class-name");
a.appendChild(text);
li.appendChild(a);
ul.appendChild(li);
}
document.querySelector("#mainNav").appendChild(ul);
}
• Use querySelector over getElementById. Both work, but theres a performance boost to querySelector. The API is close to jQuery and most (if not all) newer browsers support querySelector.
• Append the ul AFTER you've added the elements again for performance reasons.
• Use an LI to hold the link element, a second UL wont do what you want.
• Don't resuse id's, the thing you would want to use is a class, they basically do the same thing but javascript treats id's and classes differently. If that doesnt fit your needs, try a compound CSS selector in your css such as:
#mainNav ul li a { /* styles here */ }
You have to ensure that you append the correct items to the correct parents. Scanning your code I assume you want the following HTML output:
<div id="mainNav"> // this is already there
<ul>
<li>
intro.html
</li>
... // repeat for other items in the array
</ul>
</div>
You can modify your code like this to get the above result:
function write_navBar() {
var links = ["intro.html", "art.html", "portfolio.html", "guides.html", "about_me.html"]
var ul = document.createElement("ul");
document.getElementById("mainNav").appendChild(ul);
for (i = 0 ; i < links.length ; i++) {
var li = document.createElement("li");
var a = document.createElement("a");
a.href = links[i];
a.className = "some-class-to-target-in-your-css";
a.innerText = links[i];
ul.appendChild(li);
li.appendChild(a);
}
}
Your approach isn't that bad. I at one time took a similar outlook towards doing these. However, I am now of the belief it is far more testable, reliable, and easier to build these as actual templates, clone them, and populate them through a factory pattern.
In your setup, you have a parent ul, and then build multiple ul's with a's in them -- I am going to assume you meant to nest li elements -- and so that is what this will do.
Here is how I would approach that in your scenario.
Step 1: Build a the template, and create the styling and visual effect.
.navLink a{
padding:3px;
}
<ul class="menuTemplate">
<li class="navLink"><a></a></li>
</ul>
Simple style, I know. But it is just for simplicity, you can really do whatever you want there to style the example. Note that it is a simple structure, so all you are really seeing in there is a template, an li element, and the a element.
What we are also going to add to the style definition in our use case is
.menuTemplate{
display:none;
}
Because we don't actually want to see the template, we just want to use it. Next, we will create a factory for these.
var navFactory = new function(){
var template = document.querySelector('.menuTemplate .navLink');
this.Create = function(text,href){
var nav = template.cloneNode(true);
var link = nav.querySelector('a');
link.href = href;
link.innerText = text;
return nav;
}
};
The last step is to simply take your element that will hold the nav elements - you named this "mainNav" above - and fill it in from the factory.
var navFactory = new function(){
var template = document.querySelector('.menuTemplate .navLink');
this.Create = function(text,href){
var nav = template.cloneNode(true);
var link = nav.querySelector('a');
link.href = href;
link.innerText = text;
return nav;
}
};
function write_navBar() {
var links = ["intro.html", "art.html", "portfolio.html", "guides.html", "about_me.html"];
var navUl = document.querySelector('#mainNav ul');
for(var i = 0; i < links.length; i++){
navUl.appendChild(navFactory.Create(links[i],links[i]));
}
}
write_navBar();
.menuTemplate{
display:none;
}
.navLink a{
padding:3px;
opacity:0.85
}
.navLink a:hover{
opacity:1;
}
<ul class="menuTemplate">
<li class="navLink"><a></a></li>
</ul>
<div id="mainNav">
<ul></ul>
</div>

CSS style to inline style via JavaScript

I want to add all CSS styles of a specific element to its inline style attribute. For example:
I have:
<div id="d"></div>
and:
#d { background: #444444; width: 50px; height: 20px; display: inline-block; }
Now I want a JavaScript function that turns my div into this:
<div id="d" style="background: #444444; width: 50px; height: 20px; display: inline-block;"></div>
Please help me. And, by the way, I don't want any CSS styles to re-write any existing inline style.
You can do something like this:
function applyStyle(el) {
s = getComputedStyle(el);
for (let key in s) {
let prop = key.replace(/\-([a-z])/g, v => v[1].toUpperCase());
el.style[prop] = s[key];
}
}
let x = document.getElementById('my-id');
applyStyle(x);
Where x is the element you want to apply the style to.
Basically this function gets the computed style of the element and then copies each property (like padding, background, color, etc.) to the inline style of the element.
I don't know why you need to do this, but it's a really dirty approach in my opinion. I would personally advise against it.
It appears this library will do what you're looking for: https://github.com/lukehorvat/computed-style-to-inline-style
Convert a HTML element's computed CSS to inline CSS.
Uses Window.getComputedStyle internally.
This one?
function transferComputedStyle(node) {
var cs = getComputedStyle(node, null);
var i;
for (i = 0; i < cs.length; i++) {
var s = cs[i] + "";
node.style[s] = cs[s];
}
}
function transferAll() {
var all = document.getElementsByTagName("*");
var i;
for (i = 0; i < all.length; i++) {
transferComputedStyle(all[i]);
}
}
Simply call transferAll onload, or whereever.
I think the issue with the accepted answer (thank you for that!) is that one of the properties it tries to transfer on the element style from the Computed Style is the cssText.
If we exclude from the transfer cssText and also other properties that are actually methods, it works!
So building on the accepted answer and this answer, I've got:
var el = document.querySelector("#answer-25097808 > div > div.answercell.post-layout--right > div.s-prose.js-post-body > pre"); // change yourId to id of your element, or you can write “body” and it will convert all document
var els = el.getElementsByTagName("*");
for(var i = -1, l = els.length; ++i < l;){
el = els[i]
s = getComputedStyle(el)
for (let styleKey in el.style) {
for (let computedStyleKey in s) {
let computedStyleKeyCamelCase = computedStyleKey.replace(/\-([a-z])/g, v => v[1].toUpperCase());
if ((typeof el.style[styleKey] != "function") && (styleKey != 'cssText')){
if(styleKey == computedStyleKeyCamelCase) {
el.style[styleKey] = s[computedStyleKey];
}
}
}
}
}
P.S.: the above code should run in the Developer Tools console (tried it in Chrome)
Using jQuery it can be done easily. Here is the sample code:
If you are new in jQuery and you don't know how to add and work then follow this link
$(document).ready(function(){
$("#d").css('background-color', '#444444').css('width', '50px').css('height', '20px').css('display', 'inline-block');
});
For javascript code I am not confident but for jQuery I am sure that it will work.
Correct me if I am wrong.

Javascript AA Font enlargment accessibility

OK basically i need help to create a code that increase font size on a mouse click.
Here is an example:
http://www.rnib.org.uk/ in the top right corner there are 3 AAA's which increase the pages font size etc
my current code is
// JavaScript Document
var min = 12;
var max = 32;
function increaseFontSize() {
var p = document.getElementsByTagName('p');
for (i = 0; i < p.length; i++) {
if (p[i].style.fontSize) {
var s = parseInt(p[i].style.fontSize.replace("px", ""));
} else {
var s = 12;
}
if (s != max) {
s += 1;
}
p[i].style.fontSize = s + "px"
}
}
function decreaseFontSize() {
var p = document.getElementsByTagName('p');
for (i = 0; i < p.length; i++) {
if (p[i].style.fontSize) {
var s = parseInt(p[i].style.fontSize.replace("px", ""));
} else {
var s = 12;
}
if (s != min) {
s -= 1;
}
p[i].style.fontSize = s + "px"
}
}
it is implemented in the HTML like this:
-
+
mine only works for items tagged as 'p' can anyone help me create it so the function works like the RNIB.org website cheers
I think you may be overcomplicating things. I would approach this issue more from a CSS perspective with a little minor work through JS. I would:
Use a class name on a container element
Use CSS to style several different sizes
Use JS to change the class name when the plus/minus links are clicked
HTML:
Small Font
Large Font
Normal Font
<div id="myContainer" class="size-normal">
<h1>Some header</h1>
<p>Some paragraph</p>
<ul>
<li>Some list item</li>
</ul>
</div>
CSS:
#myContainer.size-normal { font-size: 12px; }
#myContainer.size-large { font-size: 14px; }
#myContainer.size-small { font-size: 10px; }
JS:
var containerEle = document.getElementById('myContainer');
function smallFontSize() {
containerEle.className = "size-small";
}
function largeFontSize() {
containerEle.className = "size-large";
}
function normalFontSize() {
containerEle.className = "size-normal";
}
If your CSS is set up so that you have a body font-size set to 100% and all element font sizes defined as 1.1 em, 1.5em, etc. Then your buttons can trigger these to increase or decrease the font size of the whole page.
document.getElementsByTagName('body')[0].style.fontSize.smaller;
document.getElementsByTagName('body')[0].style.fontSize.larger;
All elements will then change size relative to each other, e.g. your h1, h2, etc. will still be bigger than your p elements.
I would consider 'larger' and 'smaller' buttons more user-friendly than three predefined sizes.
Instead of doing this just for your site, what if you keep the icons there, but when someone presses them, you show a popup explaining that zoom/font-size increase is built-in to almost every browser out there already?
That gets around the complications of writing a script or what interval to use for the font size, plus it has the added benefit of teaching users that this functionality is already available on almost any website they use.
You can also do a little UA sniffing to determine which hot-key they should press and show that in the pop-up.
Personally, I'm not recommended to increase/decrease the font size by 1 every click. It is because you have to iterate many elements and set the font size. I will suggestion use 3-5 class to define the font-size and set to body to affect the further elements. But if you insist to increase/decrease the font size by 1 every click, you can reference the following code. If you would like to select elements from header, you can select it like this document.getElementById("menu").getElementsByTagName("h1")
function increaseFontSizeInternal(list) {
for(i=0;i<list.length;i++)
{
var s = 12;
if(list[i].style.fontSize)
{
s = parseInt(list[i].style.fontSize.replace("px",""));
}
if(s!=max)
{
s += 1;
}
list[i].style.fontSize = s+"px"
}
}
function increaseFontSize()
{
var paragraph = document.getElementsByTagName('p');
increaseFontSizeInternal(paragraph);
var links = document.getElementsByTagName('a');
increaseFontSizeInternal(links);
var headerInMenu = document.getElementById("menu").getElementsByTagName("h1")
increaseFontSizeInternal(headerInMenu);
}
function decreaseFontSizeInternal(list)
{
for(i=0;i<list.length;i++)
{
var s = 12;
if(list[i].style.fontSize)
{
s = parseInt(list[i].style.fontSize.replace("px",""));
}
if(s!=min) {
s -= 1;
}
list[i].style.fontSize = s+"px"
}
}
function decreaseFontSize()
{
var paragraph = document.getElementsByTagName('p');
decreaseFontSizeInternal(paragraph);
var links = document.getElementsByTagName('a');
decreaseFontSizeInternal(links);
var headerInMenu = document.getElementById("menu").getElementsByTagName("h1")
decreaseFontSizeInternal(headerInMenu);
}
I recommend you to just to change page zoom. This will not break the design of website.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
A+<br>
A-

changing CSS class definition

Suppose I have this class:
.MyClass{background:red;}
This class applies to several divs. I want to change the color of the background to orange by changing the color defined in MyClass.
Now, I know I could do $('.MyDiv').css('background', 'orange');
But my question is really this: how do I change the CSS class definition so that MyClass elements now have background:orange;? I want to be able to change several CSS color properties from one color to another.
Thanks.
Actually altering your stylesheet is pretty challenging. Much more easily, though, you can switch out your stylesheet for a different one, which may be sufficient for your purposes. See How do I switch my CSS stylesheet using jQuery?.
For actually altering the stylesheet content, How to change/remove CSS classes definitions at runtime? will get you started.
It is difficult to find the rule you want because you have to iterate through the document.styleSheets[i].cssRules array. (and compare your class name with the selectorText attribute)
So my solution to this problem is to add a new CSS class, remove the old CSS class from the HTML element and add this class instead of it.
var length = getCssRuleLength();
var newClassName = "css-class-name" + length;
//remove preview css class from html element.
$("#your-html-element").removeClass("css-class-name");
$("#your-html-element").removeClass("css-class-name" + (length-1));
$("#your-html-element").addClass(newClassName);
//insert a css class
insertCssRule("." + newClassName + ' { max-width: 100px; }', length);
function getCssRuleLength() {
var length = 0;
if (document.styleSheets[1].cssRules) {
length = document.styleSheets[1].cssRules.length;
} else if (document.styleSheets[1].rules) { //ie
length = document.styleSheets[1].rules.length;
}
return length;
}
function insertCssRule(rule, index) {
if (document.styleSheets[1].cssRules) {
document.styleSheets[1].insertRule(rule, index);
} else if (document.styleSheets[1].rules) { //ie
document.styleSheets[1].addRule(rule, index);
}
}
Here's my answer in case anyone stumbles upon this. Give your elements a new class name that doesn't already exist, then dynamically add a style segment:
var companyColor = 'orange' //define/fetch the varying color here
var style = '<style>.company-background {background-color: ' + companyColor + '; color: white;}</style>';
$('html > head').append($(style));
//give any element that needs this background color the class "company-background"
You have 2 options
add a new stylesheet that overrides this .MyClass
have a second class with the different property, and change the class Name on these elements
Looking at your question, I think a better approach is to switch MyClass with something else using JavaScript rather than to change the properties of the class dynamically.
But if you are still keen you can switch CSS stylesheets with jQuery http://www.cssnewbie.com/simple-jquery-stylesheet-switcher/
var changeClassProperty = function(sheetName, className, propertyName, newValue, includeDescendents) {
var ending = '$';
setValue = '';
if (includeDescendents === true) {
ending = '';
}
if (typeof(newValue) != 'undefined') {
setValue = newValue;
}
var list = document.styleSheets;
for (var i = 0, len = list.length; i < len; i++) {
var element = list[i];
if (element['href'] && element['href'].match(new RegExp('jquery\.qtip'))) {
var cssRules = element.cssRules;
for (j = 0, len2 = cssRules.length; j < len2; j++) {
var rule = cssRules[j];
if (rule.selectorText.match(new RegExp(className + ending))) {
cssRules[j].style.backgroundColor = setValue;
console.log(cssRules[j].style.backgroundColor);
}
}
}
}
}
changeClassProperty('jquery.qtip', 'tipsy', 'backgroundColor', 'yellow');
You'd be much better off adding and removing classes instead of attempting to change them.
For example
.red {
background: red;
}
.orange {
background: orange;
}
$('#div').click(function(){
$(this).removeClass('red').addClass('orange');
});

Categories

Resources