I am using Google Chrome's developer tools to inspect CSS styles. Sometimes I need to compare the styles of 2 elements on the page, or 2 elements on different pages.
Is there a tool or add-on that would allow me to easily compare? Right now I have to visually look, switching back and forth, comparing one thing at a time. I wish there was a tool that would highlight the differences in styles, source, ...
I am open to use another browser if such a tool exists.
This should help you compare computed style differences, for two elements, on the same page (I'm not sure about how to approach two elements on different pages):
function styleDifferences(a, b) {
var as = getComputedStyle(a, null);
var bs = getComputedStyle(b, null);
var r = [];
for (var i in as)
if (as[i] !== bs[i])
r.push(i + ' differs: ' + as[i] + ' | ' + bs[i]);
return r.join('\n');
}
Example:
>>> styleDifferences(document.body, document.querySelector('pre'));
backgroundColor differs: rgb(255, 255, 255) | rgb(238, 238, 238)
borderCollapse differs: separate | collapse
fontFamily differs: Arial,Liberation Sans,DejaVu Sans,sans-serif | Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif
fontSize differs: 12.8px | 13.7px
height differs: 1928.28px | auto
lineHeight differs: 12.8px | 17.8px
marginBottom differs: 0px | 10px
maxHeight differs: none | 600px
overflow differs: visible | auto
paddingTop differs: 0px | 5px
paddingRight differs: 0px | 5px
paddingBottom differs: 0px | 5px
paddingLeft differs: 0px | 5px
textAlign differs: center | left
whiteSpace differs: normal | pre
width differs: 1423px | auto
MozColumnGap differs: 12.8px | 13.7px
overflowX differs: visible | auto
overflowY differs: visible | auto
Related
I'm aware of fluidPage() and fixedPage() layouts for shiny. In my case though it would be nice to have another behaviour for elements (plots/input fields/shinydashboard boxes).
Elements should have a fixed width (and height) and move automatically to the next row if the display width changes.
Legend:
[...] <- Element
| <- Right browser window border
Examples:
1. Big screen case
[...] [..] [.....] [...] [...] |
2. Small screen case
[...] [..] [.....] [...] |
[...] |
3. Even smaller screen case
[...] [..] |
[.....] |
[...] [...] |
Is a layout like this possible with shiny/shinydashboard?
Thanks to #SimonLarsen I was able to find a solution. Shiny offers flowLayout() which supports this kind of layout. Unfortunately shinydashboard boxes can't be used within this framework, because they expect width values within the bootstrap grid framework. You would have to change the implementation of shinydashbaord::box() to work with pixel width values and that would cause all sorts of other problems down the line.
I've opted for the following solution:
shiny::fluidRow(
shinydashboard::box(
width = 12,
shiny::div(
style = "overflow-x: scroll",
shiny::flowLayout(
cellArgs = list(
style = "
min-width: 300px;
width: auto;
height: auto;
border: 1px solid darkgray;
padding: 10px;
margin: 10px;
"),
plotly::plotlyOutput(
width = "500px",
ns("plot1")
),
plotly::plotlyOutput(
width = "500px",
ns("plot1")
),
plotly::plotlyOutput(
width = "1045px",
ns("plot2")
)
)
)
)
)
I build my own boxes with fixed height and for each plot/content element an individually defined width.
I'm trying to emulate the CTRL+F functionality from Chrome that highlights matches on the page in the scrollbar, but for certain fields in a form. Using page offsets and percentages, I have blocks of color which correspond to the relative locations of those fields on the page.
In my prototype, the blocks of color sit to the left of the scrollbar. Ideally, they'd sit UNDERNEATH the scrollbar, and the scrollbar's track would be transparent so that it looks like they're part of the scrollbar track.
Can the default scrollbar be set to allow for overflow content to show underneath it (or allow page content to go over it)? I know this could be accomplished if I just rolled my own scroll, but I'd like to use the default ones provided by the browser if at all possible.
It's clearest if you just look at this Prototype.
CSS:
::-webkit-scrollbar {
width: 14px;
height: 18px;
background-color:transparent;
}
::-webkit-scrollbar-track,
::-webkit-scrollbar-track-piece {
background:none;
}
::-webkit-scrollbar-thumb {
height: 6px;
border: 4px solid rgba(0, 0, 0, 0);
background-clip: padding-box;
-webkit-border-radius: 7px;
background-color: #333
}
::-webkit-scrollbar-button {
width: 0;
height: 0;
display: none;
}
::-webkit-scrollbar-corner {
background-color: transparent;
}
I thought of rendering the matches on the trackbar similarly to what browsers do today before. The idea is simple by using linear-gradient background for the ::-webkit-scrollbar-track. However I did not try implementing this. Right after reading your question, I've tried it and looks like it's not such easy.
You can use the linear-gradient background OK, but if you try rendering more than 1 match (a line), it sometimes can't be rendered (especially when the window's size is changed) and the line is not rendered smoothly. Such as this seems to be OK:
//render 2 lines, one is at 50px and the other is at 100px
background: linear-gradient(transparent 50px, red 50px, red 51px, transparent 51px,
transparent 100px, red 100px, red 101px, transparent 101px);
but it's not stable, as I said when you try resizing the window, at some size, some line won't be rendered (at least I tried on Opera). When the window's height is large, the line even becomes blurred (not sharp) and thicker. I don't really understand this, because the color stops are set fixedly (by px, not by %). This issue is even worse when the number of lines is larger. You have a linear-gradient with many corresponding color stops. That seems to be a neat way to solve the problem. Just because of the undesired issue, we can't use that approach.
The new approach: So I tried using multi-backgrounds feature instead. Each background just renders 1 line, the background-size is the same for all the background is just about 2px height and the background-position should be different. Here is the equivalent code (to the above clean code) using this approach:
background: linear-gradient(red, red), linear-gradient(red, red);
background-repeat: no-repeat;
background-size: 100% 2px;
background-position: 0 50px, 0 100px;
The new approach of course requires that the browser has to support multi-backgrounds features (looks like just IE8- do not support this cool feature).
So that's almost what you need to solve this problem. Now we need to find how to apply that style using script. We can't select a pseudo-element (or something like that) via script. We can just use the window.getComputedStyle() method to get the read-only style of a pseudo-element. However we always have a way to modify the CSS directly. That's is by using pure JS with the help of document.styleSheets and cssRules. They allow us to insert/remove/modify a rule.
That looks great. But there is still another issue. When changing the style using that method, the style is not applied right (at least it happens to the ::-webkit-scrollbar-track, it may not happen to other elements). Only when you move the mouse over the scrollbar, the new style is applied. I've just found a simple way to invalidate that scrollbar by setting the overflow of document.documentElement (the html) to hidden and set it back to auto. That works almost well.
Here is the code:
var requiredTb = $(".required input");
var invalids = requiredTb;
var offsets = [];
//init offsets to highlight on the trackbar later
requiredTb.each(function() {
offsets.push(($(this).offset().top)/document.body.scrollHeight * 100);
});
//find the rule styling the -webkit-scrollbar-track
//we added in the CSS stylesheet, this is done just 1 time
var sheets = document.styleSheets;
var trackRule;
for(var i = 0; i < sheets.length; i++){
var rules = sheets[i].cssRules || sheets[i].rules;
for(var j = 0; j < rules.length; j++){
var rule = rules[j];
if(rule.selectorText == "::-webkit-scrollbar-track:vertical"){
trackRule = rule; break;
}
}
}
//define an invalidate() method, we need to use this method
//to refresh the scrollbars, otherwise the newly applied style is not affected
window.invalidate = function(){
$(document.documentElement).css('overflow','hidden');
setTimeout(function(e){
$(document.documentElement).css('overflow','auto');
},1);
};
//this is the main function to set style for the scrollbar track.
function setTrackHighlights(positions, color){
positions.sort();//ensure that the input array should be ascendingly sorted.
trackRule.style.cssText = "";
var gradient = "background: ", backPos = "background-position: ";
var winHeight = $(window).height();
$.each(positions, function(i,pos){
gradient += "linear-gradient(" + color + ", " + color + "),";
backPos += "0 " + pos + "%,"
});
gradient = gradient.substr(0,gradient.length-1) + ";";
backPos = backPos.substr(0,backPos.length -1) + ";";
trackRule.style.cssText += gradient + backPos + "background-repeat:no-repeat; background-size:100% 2px";
invalidate();
}
//initially set the highlights on the trackbar
setTrackHighlights(offsets,'red');
//handle the oninput event to update the highlights accordingly
requiredTb.on('input', function(e){
var required = $(this).closest('.required');
var refreshHighlights = false;
if(this.value && !required.is('.ok')) {
required.addClass('ok');
refreshHighlights = true;
invalids = invalids.not(this);
}
if(!this.value && required.is('.ok')) {
required.removeClass('ok');
refreshHighlights = true;
invalids = invalids.add(this);
}
if(refreshHighlights){
offsets.splice(0);
invalids.each(function() {
offsets.push(($(this).offset().top)/document.body.scrollHeight * 100);
});
setTrackHighlights(offsets,'red');
}
});
You have to add an empty ::-webkit-scrollbar-track:vertical rule (we need to deal only with the vertical scrollbar) in the CSS code, it should be appended at the last to override any similar rule before. We can in fact use the insertRule() method (of a CSSRuleList which can be accessed via cssRules property) to add a new rule instead of looping through the styleSheets, and through the cssRules (of each sheet) to find the empty rule ::-webkit-scrollbar-track:vertical.
The code I posted here can be improved, such as you can add another method setTrackHighlights to allow to add more lines (instead of rendering all the lines each time we need to add/remove just 1 line)...
Note that by using the term line, I mean the rendering representation of a match on the trackbar.
Demo
As you may know that box-shadow is not a part of box-model. so what could be a good way to compute the width of box-shadow that adds to an element?
Update: I need to know the total width of an element, including the shadow width.
well you could simply add a margin equal to the box-shadow. For example:
box-shadow: 0 0 10px #008800;
margin: 10px;
in the case you use the X and Y offsets on the box-shadow use add that value to the length of the shadow. Example:
box-shadow: 5px 5px 10px #080;
margin: 5px 15px 15px 5px;
here the offset is 5px, plus the 10px length. In the case of the spread we can continue to add to the margin values to take this into consideration.
box-shadow: 5px 5px 10px 7px #080;
margin: 12px 21px 21px 12px;
using the margin will keep the shadow from overlapping other objects on the page.
Exact width will differ from browser to browser. Each renders the shadows different. If i have to give a hard calculation for the object I guess it would be the something like this (the css property for reference)
box-shadow: h-shadow v-shadow blur spread color;
The box model offsets would be
top = (spread - v_shadow + 0.5*blur)
right = (spread + h_shadow + 0.5*blur)
bottom = (spread + v_shadow + 0.5*blur)
left = (spread - h_shadow + 0.5*blur)
The coefficient of the blur is a estimate, it may need to be adjusted slightly. Personally I prefer to not use the offset, but is here to show where it would be used
here is a jsfiddle to see it in action http://jsfiddle.net/YvqZV/4/
Just extending #samuel.molinski's answer by creating a complete function that takes a box shadow and returns the widths.
function getBoxShadowWidths(boxShadow) {
// not supporting multiple box shadow declarations for now
if ((boxShadow.match(/(rgb|#)/g) || []).length > 1) {
return false;
}
const regEx = /(\d(?=(px|\s)))/g;
const matches = [];
// box-shadow can have anywhere from 2-4 values, including horizontal offset, vertical offset,
// blur, and spread. Below finds each one and pushes it into an array (regEx.exec when used in succession
// with a global regex will find each match.
let match = regEx.exec(boxShadow);
while (match != null) {
matches.push(match[0]);
match = regEx.exec(boxShadow);
}
// default blur & spread to zero px if not found by the regex
const [hOffset = 0, vOffset = 0, blur = 0, spread = 0] = matches.map(parseFloat);
// calculate approximate widths by the distance taken up by each side of the box shadow after normalizing
// the offsets with the spread and accounting for the added distance resulting from the blur
// See https://msdn.microsoft.com/en-us/hh867550.aspx - "the blurring effect should approximate the
// Gaussian blur with a standard deviation equal to HALF of the blur radius"
const top = spread - vOffset + 0.5 * blur;
const right = spread + hOffset + 0.5 * blur;
const bottom = spread + vOffset + 0.5 * blur;
const left = spread - hOffset + 0.5 * blur;
return { top, right, bottom, left };
}
Thanks #Joey for the function. I added support for multiple values:
function getBoxShadowWidths(boxShadowValues) {
const regEx = /(\d(?=(px|\s)))/g
const widths = { top: 0, right: 0, bottom: 0, left: 0 }
boxShadowValues.split(/\s*,\s*/).forEach(boxShadowValue => {
const matches = []
// box-shadow can have anywhere from 2-4 values, including horizontal offset, vertical offset, blur, and spread.
// Below finds each one and pushes it into an array (regEx.exec when used in succession with a global regex will find each match.
let match = regEx.exec(boxShadowValue)
while (match != null) {
matches.push(match[0])
match = regEx.exec(boxShadowValue)
}
// default blur & spread to zero px if not found by the regex
const [hOffset = 0, vOffset = 0, blur = 0, spread = 0] = matches.map(parseFloat)
// calculate approximate widths by the distance taken up by each side of the box shadow after normalizing
// the offsets with the spread and accounting for the added distance resulting from the blur
// See https://msdn.microsoft.com/en-us/hh867550.aspx - "the blurring effect should approximate the
// Gaussian blur with a standard deviation equal to HALF of the blur radius"
const actualWidths = {
top: spread - vOffset + 0.5 * blur,
right: spread + hOffset + 0.5 * blur,
bottom: spread + vOffset + 0.5 * blur,
left: spread - hOffset + 0.5 * blur,
}
Object.keys(actualWidths).forEach(side => {
widths[side] = Math.max(widths[side], actualWidths[side])
})
})
return widths
}
I have a situation like this:
+------------------------------------------------------------------------------+
| |
| +---------------------------------------------------+ |
| | | |
| <- fixed width -> | <- flexible width -> | |
| | | |
| +---------------------------------------------------+ |
| |
| <- flexible width -> |
| |
+------------------------------------------------------------------------------+
What this is supposed to represent is an outer DIV and its child (also a DIV) appearing in the browser. The outer div takes up, say, 90% of the viewable area (width: 90%). If the screen is resized it will resize. It's OK for it to have a minimum width.
The child div is meant to stay a fixed number of pixel from the left of the outer DIV (left: 200px).
I would like it to resize with its parent (i.e. childWidth = (parentWidth - 200) * .9).
Is it possible to do this with CSS / how?
Yes, just add right: 0px to the inner div in addition to left.
(also, try jsfiddle.net instead of ascii ;))
i have for example five divs, that has float:left and they are wrapped inside div, that has display:inline-block and width:auto. So result is one row with 5 divs and that row has width = sum of childs, because width:auto on div, that has display:inline-block results in width to fit content.
Then i have all wrapped inside div, that has width = width of one of that 5 divs and overflow:hidden, so only one of that 5 divs is visible.
But problem is, that 5 divs is not now in row, but is in column, because their parent is wrapped inside div with width = width of one of that 5 divs.
I need animate margin-left on first of that 5 divs, so the next div becomes visible. But when that divs are in column and not in row, the look and feel while animating is not what i want.
So how make something like this:
------1-------
| -----------|---------2----------
| | ----3--- | -----3-- --3----- |
| | | | | | | | | |
| | | | | | | | | |
| | -------- | -------- -------- |
| | | |
| -----------|--------------------
--------------
Only 1 must be visible.
1 has width = width of 3 and overflow: hidden, so only first of 3 is visible.
2 has display:inline-block and width:auto, so its width fit content.
3 has float left or display:inline-block.
Problem is when i wrap 2 into 1, then width of 2 is not to fit conent, but is width of 1 and becomes column and not row.
<div-1 style="overflow:hidden;width:64px;height:64px">
<div-2 style="display:inline-block;width:auto">
<div-3 style="width:64px;height:64px;float:left"></div>
<div-3 style="width:64px;height:64px;float:left"></div>
<div-3 style="width:64px;height:64px;float:left"></div>
</div>
</div>
Div-2 is row, but when i wrap it inside div-1, it becomes column and that is what is unexpected.
Sorry for my english.
First you should add clearfix to the div2 if you are floating its contents.
You could calculate the width with
var width = $('.div2').innerHeight();
$('.div2').css('width', width);
Here an example, set the .div1 { overflow: visible} to see the result:
http://jsfiddle.net/karameloso/Apz7B/
Have a look at the carouFredSel jQuery plugin, it automatically resizes the slider on the fly to fit the new slide's content.