parse css gradient rule with Javascript regex
Parsing CSS can be far more complex there are few things to remember:
- Avoid writing a parser - someone else will probably have written one already (search).
- Your parser will likely fail if you don't control the input source or test it thoroughly with input samples.
- In the case of gradients you can have "angles" as well as "corner-sides" like "right".
- There are an unknown number of color stops (minimum 1).
- You will never likely want to include the complete list of CSS colors in a regular expression (e.g.
red
,blue
, etc). - You should check out MDN for details of syntax variations, the sample code below only supports the standard syntax.
- Regular expression support and bugs are different depending on the browser and version - test your target browsers with all your samples.
OK, so here is a crazy example of how you "could" parse the gradient using regular expressions - I'm not saying you should.
Here I build my regular expressions in code to keep some level of readability and maintainability of the code.
The final output of the test_this_thing
functions console.log(result);
is as follows:
Input:
background-image:linear-gradient(to right bottom, #FF0000 0%, #00FF00 20px, rgb(0, 0, 255) 100%);
Output:
{
original:"to right bottom, #FF0000 0%, #00FF00 20px, rgb(0, 0, 255) 100%",
line:"to right bottom",
sideCorner:"right bottom",
colorStopList:[
{
color:"#FF0000",
position:"0%"
},
{
color:"#00FF00",
position:"20px"
},
{
color:"rgb(0, 0, 255)",
position:"100%"
}
]
}
Note the output includes the original
property - this looks like the input - but if part of the input wasn't matched the input
and original
values would be different; noting an possible error in the parser.
Here is a source:
/**
* Utility combine multiple regular expressions.
*
* @param {RegExp[]|string[]} regexpList List of regular expressions or strings.
* @param {string} flags Normal RegExp flags.
*/
var combineRegExp = function (regexpList, flags) {
var i,
source = '';
for (i = 0; i < regexpList.length; i++) {
if (typeof regexpList[i] === 'string') {
source += regexpList[i];
} else {
source += regexpList[i].source;
}
}
return new RegExp(source, flags);
};
/**
* Generate the required regular expressions once.
*
* Regular Expressions are easier to manage this way and can be well described.
*
* @result {object} Object containing regular expressions.
*/
var generateRegExp = function () {
// Note any variables with "Capture" in name include capturing bracket set(s).
var searchFlags = 'gi', // ignore case for angles, "rgb" etc
rAngle = /(?:[+-]?\d*\.?\d+)(?:deg|grad|rad|turn)/, // Angle +ive, -ive and angle types
rSideCornerCapture = /to\s+((?:(?:left|right)(?:\s+(?:top|bottom))?))/, // optional 2nd part
rComma = /\s*,\s*/, // Allow space around comma.
rColorHex = /\#(?:[a-f0-9]{6}|[a-f0-9]{3})/, // 3 or 6 character form
rDigits3 = /\(\s*(?:\d{1,3}\s*,\s*){2}\d{1,3}\s*\)/,// "(1, 2, 3)"
rDigits4 = /\(\s*(?:\d{1,3}\s*,\s*){2}\d{1,3}\s*,\s*\d*\.?\d+\)/,// "(1, 2, 3, 4)"
rValue = /(?:[+-]?\d*\.?\d+)(?:%|[a-z]+)?/,// ".9", "-5px", "100%".
rKeyword = /[_a-z-][_a-z0-9-]*/,// "red", "transparent", "border-collapse".
rColor = combineRegExp([
'(?:', rColorHex, '|', '(?:rgb|hsl)', rDigits3, '|', '(?:rgba|hsla)', rDigits4, '|', rKeyword, ')'
], ''),
rColorStop = combineRegExp([rColor, '(?:\\s+', rValue, '(?:\\s+', rValue, ')?)?'], ''),// Single Color Stop, optional %, optional length.
rColorStopList = combineRegExp(['(?:', rColorStop, rComma, ')*', rColorStop], ''),// List of color stops min 1.
rLineCapture = combineRegExp(['(?:(', rAngle, ')|', rSideCornerCapture, ')'], ''),// Angle or SideCorner
rGradientSearch = combineRegExp([
'(?:(', rLineCapture, ')', rComma, ')?(', rColorStopList, ')'
], searchFlags),// Capture 1:"line", 2:"angle" (optional), 3:"side corner" (optional) and 4:"stop list".
rColorStopSearch = combineRegExp([
'\\s*(', rColor, ')', '(?:\\s+', '(', rValue, '))?', '(?:', rComma, '\\s*)?'
], searchFlags);// Capture 1:"color" and 2:"position" (optional).
return {
gradientSearch: rGradientSearch,
colorStopSearch: rColorStopSearch
};
};
/**
* Actually parse the input gradient parameters string into an object for reusability.
*
*
* @note Really this only supports the standard syntax not historical versions, see MDN for details
* https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient
*
* @param regExpLib
* @param {string} input Input string in the form "to right bottom, #FF0 0%, red 20px, rgb(0, 0, 255) 100%"
* @returns {object|undefined} Object containing break down of input string including array of stop points.
*/
var parseGradient = function (regExpLib, input) {
var result,
matchGradient,
matchColorStop,
stopResult;
// reset search position, because we reuse regex.
regExpLib.gradientSearch.lastIndex = 0;
matchGradient = regExpLib.gradientSearch.exec(input);
if (matchGradient !== null) {
result = {
original: matchGradient[0],
colorStopList: []
};
// Line (Angle or Side-Corner).
if (!!matchGradient[1]) {
result.line = matchGradient[1];
}
// Angle or undefined if side-corner.
if (!!matchGradient[2]) {
result.angle = matchGradient[2];
}
// Side-corner or undefined if angle.
if (!!matchGradient[3]) {
result.sideCorner = matchGradient[3];
}
// reset search position, because we reuse regex.
regExpLib.colorStopSearch.lastIndex = 0;
// Loop though all the color-stops.
matchColorStop = regExpLib.colorStopSearch.exec(matchGradient[4]);
while (matchColorStop !== null) {
stopResult = {
color: matchColorStop[1]
};
// Position (optional).
if (!!matchColorStop[2]) {
stopResult.position = matchColorStop[2];
}
result.colorStopList.push(stopResult);
// Continue searching from previous position.
matchColorStop = regExpLib.colorStopSearch.exec(matchGradient[4]);
}
}
// Can be undefined if match not found.
return result;
};
var test_this_one = function (regExpLib, input) {
var result,
rGradientEnclosedInBrackets = /.*gradient\s*\(((?:\([^\)]*\)|[^\)\(]*)*)\)/,// Captures inside brackets - max one additional inner set.
match = rGradientEnclosedInBrackets.exec(input);
if (match !== null) {
// Get the parameters for the gradient
result = parseGradient(regExpLib, match[1]);
if (result.original.trim() !== match[1].trim()) {
// Did not match the input exactly - possible parsing error.
result.parseWarning = true;
}
} else {
result = "Failed to find gradient";
}
return result;
};
var test_this_thing = function () {
var result = [],
regExpLib = generateRegExp(),
testSubjects = [
// Original question sample
'background-image:linear-gradient(to right bottom, #FF0000 0%, #00FF00 20px, rgb(0, 0, 255) 100%);',
// Sample to test RGBA values (1)
'background-image:linear-gradient(to right bottom, rgba(255, 0, 0, .1) 0%, rgba(0, 255, 0, 0.9) 20px);',
// Sample to test optional gradient line
'background-image:linear-gradient(#FF0000 0%, #00FF00 20px, rgb(0, 0, 255) 100%);',
// Angle, named colors
'background: linear-gradient(45deg, red, blue);',
// Gradient that starts at 60% of the gradient line
'background: linear-gradient(135deg, orange, orange 60%, cyan);',
// Gradient with multi-position color stops
'background: linear-gradient(to right, red 20%, orange 20% 40%, yellow 40% 60%, green 60% 80%, blue 80%);'
];
for (var i = 0; i < testSubjects.length; i++) {
result.push(test_this_one(regExpLib, testSubjects[i]));
}
console.log(result);
};
test_this_thing();
Split linear-gradient into an object
With Regular expression
we can define what parts we want from string.
// Define string
var str = 'linear-gradient(to left top, #F0F calc(30% - 6px), hsl(100, 100%, 25%) 75%, yellow)';
// Get string between first ( and last )
str = str.substring(str.indexOf('(') + 1, str.lastIndexOf(')'));
// Finally with regex we can get each parts separatelly
console.log( str.split( /,(?![^(]*\))(?![^"']*["'](?:[^"']*["'][^"']*["'])*[^"']*$)/ ) );
And output will be:
(4) [Array]
"to left top"
"#F0F calc(30% - 6px)"
"hsl(100, 100%, 25%) 75%"
"yellow"
swift split css linear-gradient using regex
Can you try this expression? This should get you what you wanted in groups
linear-gradient\((\d+)deg|,\s+(?:(?:rgba\((.*?)\)).+?(\d+)%)+
Extract RGB and RGBA from css Rules in JavaScript
I want to extract all
rgba
andrgb
from the stirng
I hope you don't want to validate the digits in rgba and rgb, if yes then get the matched group from index 1 using capturing groups.
(rgba?\((?:\d{1,3}[,\)]){3}(?:\d+\.\d+\))?)
Live demo
You can get the values using Lazy
way as well in the same way from matched group index 1.
(rgba?.*?\))
Live Demo
Last Pattern explanation:
( group and capture to \1:
rgb 'rgb'
a? 'a' (optional)
.*? any character except \n (0 or more times)
\) ')'
) end of \1
regular expression with css properties
Search for the beginning of the line or a ";" as well (or possibly a line-break/tab depending on your string formatting):
/(^|;)[^:;]*:/
You still have to cleanup your results a bit though.
Alternatively, you could split the string on ";" first, then split each bit on ":" and grab the 0th member of each for your properties:
var str = "filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#cccccc', endColorstr='#000000');background: -webkit-linear-gradient(top, black, white);animation-duration: 12s;";
var rules = str.split(";")
var i, l = rules.length;
var properties = [];
for( i = 0; i < l; i++ ){
properties.push( rules[ i ].split(":")[0] ); //dangerous if you're not sure you'll always have a result
}
Using Regex to match function calls containing parentheses
To match the function in the CSS used in your examples, use this regex:
\blinear-gradient\(([^()]*|\([^()]*\))*\)
It will match one level of nested parenthesis.
See regex demo here.
See JavaScript usage demo here.
Related Topics
Hover Over a Hidden Element to Show It
How to Get Screen Position of CSS3-3D Transformed Elements
Customize Zoom In/Out Button in Leaflet.Js
How to Color Specific Letters in HTML Element Text
Change the :Before Selector from JavaScript
How to Add a Drop Shadow to Chart.Js Line Chart
How to Disable JavaScript for Responsive Design
Click and Hold Event Listener with JavaScript
How to Add a Classname to a CSS Variable
Columns of Equal Height with Jquery
Get Computed Value of CSS Variable That Uses an Expression Like Calc
Fastest Selector Method in Jquery and CSS - Id or Not
How to Fix the Bootstrap Navbar to the Top After a Div Was Scrolled
Spring Not Finding Resource Files (Css, Jsp...)
Jquery Position Div Fixed at Top on Scroll