Add page-break function to Vue-Multipane component - javascript

I'm having problems with Vue-multipane component at my use-case and I don't find a solution.
I'm trying to disable the multipane once the screen size is smaller than 768px and display full-width rows instead of the columns. So my page is better usable on mobile.
Now I'm having two problems:
Shrinked pane doesn't get maximized to 100% if screen size is below 768px (see screenshots below). So first reduce the width of the pane with the handle and then reduce the window width below the break point 786px to see the mentioned behavior.
Handle isn't visible if reducing the screen size to 768 - 770px (close to the page-break). Page break not active but handle isn't visible. Not sure what's wrong - maybe that's easy to fix but I couldn't find a way yet.
(missing handle)
Shrinked to 767px (should be maximized to 100%) - reduce pane than reduce width of window:
It should look like this (on page load it is displayed correctly):
Please have a look at this fiddle or the code below.
But it's better to go to jsfiddle because it's easier to resize and test the mentioned behavior.
What I've tried to solve the issues:
Add css styles flex-grow: 1 and width: 100%; into the media query of the left-pane but that's not working.
For the handle issue: I've changed the break point position a bit but the behaviour was still there.
Note: I've tested the code with Firefox.
//console.log(Multipane, window)
const PageBreak = {
data() {
return {
screenWidth: document.documentElement.clientHeight,
}
},
computed: {
largeScreen () {
return this.screenWidth > 768; // 10px for handle
}
},
// bind event handlers to the `handleResize` method (defined below)
mounted: function () {
window.addEventListener('resize', this.handleResize)
},
beforeDestroy: function () {
window.removeEventListener('resize', this.handleResize)
},
methods: {
// whenever the document is resized, re-set the 'fullHeight' variable
handleResize (event) {
this.screenWidth = document.documentElement.clientWidth
}
}
}
new Vue({
el: '#app',
mixins: [ PageBreak ]
})
.custom-resizer {
width: 100%;
height: 400px;
}
.custom-resizer > .pane {
text-align: left;
padding: 15px;
overflow: hidden;
background: #eee;
border: 1px solid #ccc;
}
.custom-resizer > .multipane-resizer {
margin: 0;
left: 0;
position: relative;
}
.custom-resizer > .multipane-resizer:before {
display: block;
content: "";
width: 3px;
height: 40px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -20px;
margin-left: -1.5px;
border-left: 1px solid #ccc;
border-right: 1px solid #ccc;
}
.custom-resizer > .multipane-resizer:hover:before {
border-color: #999;
}
.left-pane {
width: 50%;
}
#media (max-width: 768px) {
/* stack panes if smaller than 768px*/
.multipane.layout-v {
/*flex-direction: column;*/
display: block;
}
.left-pane {
/*flex-grow: 1;*/
/* how to maximize left-pane if it was shrinked before? */
width: 100%;
}
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.5.3/css/bulma.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.js"></script>
<script src="https://unpkg.com/vue-multipane#0.9.5/dist/vue-multipane.min.js"></script>
<div id="app">
<multipane class="custom-resizer" layout="vertical">
<div class="pane left-pane">
<div>
<h6 class="title is-6">Pane 1</h6>
</div>
</div>
<multipane-resizer v-if="largeScreen"></multipane-resizer>
<div class="pane" :style="{ flexGrow: 1 }">
<div>
<h6 class="title is-6">Pane 2</h6>
</div>
</div>
<!--<multipane-resizer></multipane-resizer>
<div class="pane" :style="{ flexGrow: 1 }">
<div>
<h6 class="title is-6">Pane 3</h6>
</div>
</div>-->
</multipane>
</div>

I think I've found a way to solve the issues. (The handle issue fix with the page break position is a bit hacky but works.)
Let me explain what I did:
I added class full_width to the left pane if the screen is smaller than 768px to maximize the pane to width: 100% !important with this Vue code :class="{full_width: !largeScreen}"
The handle was missing if I'm setting the page break position to 768px. I've reduced the position by 17px (looks like the padding 15px + 2px from border - but I couldn't change this with css) then it's working. So inside the computed property of largeScreen I'm returning it like this: return this.screenWidth > (768 - 17); (I've tested this by console logging the screen size.)
Please have a look at this fiddle.
I've you're having an explanation to the handle issue and why it's working if I'm changing the position by 17px - please let me know.
The solution is OK and seems to work for me. Maybe I'm filing an issue at Vue-multipane repo. for a feature request because it's probably easier if it's directly added in the Multipane component and it would be nice if passing the break-point with a property would work.

Related

Strange transition behavior for inline elements styles in certain places

This is a jsfiddle example file that replicates the problem: https://jsfiddle.net/Lhr0d6cw/11/
I wanted the element (when clicked) to expand for 6seconds from its original position but notice that when you click the red card (or any card), it doesn't start expanding from the originals position it used to be, but rather from the middle, I assume that its because transition of 6s to top and left is not being applied for some reason.
Only places I was able to make it work properly so far are stackoverflow editor below or by inserting a debugger in the code and doing it manually but when using my localhost or jsfiddle it doesn't transition properly.
This is the same example on stackoverflow which works as desired:
const productCards = document.querySelectorAll(".products__card");
productCards.forEach(c => {
// console.log("clicked1");
c.addEventListener("click", openCard)
});
function openCard(e) {
console.log("clicked");
console.dir(this);
let top = this.getBoundingClientRect().top;
let left = this.getBoundingClientRect().left;
// this.style.transition = "top 0.9s, left 0.9s";
this.style.top = top + "px";
this.style.left = left + "px";
this.style.position = "fixed";
console.log(`top: ${top}, left: ${left}`);
// debugger;
this.classList.add("open");
}
.products {
display: flex;
flex-wrap: wrap;
flex-direction: row;
justify-content: center;
min-width: 1000px;
max-width: 1500px;
margin-bottom: 300px;
}
.products .products__card {
display: flex;
flex-direction: column;
width: 150px;
height: 250px;
margin-bottom: 30px;
margin-right: 30px;
margin-left: 30px;
background-color: red;
transform: scale(1);
/* box-shadow: 3px 7px 55px -10px c(very-light); */
transition: width 0.9s, height 0.9s, z-index 0.9s, top 6s, left 6s;
}
.products .products__card.card-1 {
background-color: red;
}
.products .products__card.card-2 {
background-color: blue;
}
.products .products__card.card-3 {
background-color: green;
}
.products .products__card.card-4 {
background-color: yellow;
}
.products .products__card.card-5 {
background-color: pink;
}
.products .products__card.card-6 {
background-color: gray;
}
.products .products__card.open {
width: 550px;
height: 800px;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
z-index: 120;
box-shadow: 0 0 1000px 1000px c(box-overlay);
}
<div class="products">
<div class="products__card card-1">
</div>
<div class="products__card card-2">
</div>
<div class="products__card card-3">
</div>
<div class="products__card card-4">
</div>
<div class="products__card card-5">
</div>
<div class="products__card card-6">
</div>
</div>
works when debugging:
The strange thing as mentioned above is that my problem in the browser using localhost is also solved when I insert debugger in the code and manually skip through the last step of adding .open class. If you have the same problem in jsfiddle or your own editor, try adding debugger; before this.classList.add("open"); and then open the console and then click the card and go over the last step manually in the console. you will notice that the card expanded from its original place as desired taking 6s to finish which means the transition was applied in this case.
My questions:
Why is transition for top and left only working in certain environments? is it a browser problem? I'm using the latest chrome. does someone know of a better way to achieve the same results?
code comments:
-obviously, 6 seconds is not what I will be using in my code, its used here just to make the transition obvious.
-In my source code, you can see that because I can't transition from position static to position fixed I had to use Javascript to add position fixed style inline to the element before the .open class is added, that way transition can take place properly when .open is added.
-I also added top and left values inline to keep the card in its original place when position: fixed style is applied because as you might know fixed position takes the element out of its flow, so top and left keep it in place.
-I added !important in css .open class because without it I can't override inline css as you might also know.
Thank you
I was able to solve my problem just now by applying a little hack. It seems that in some environments (localhost, jsfiddle) the javascript engine is adding the .open class faster than expected and the fact that it is working fine when debugging (slow process) indicated that to me. so I added a setTimeout() to the last piece of code delayed it by 20. this solved my problem and now it works fine on JSfiddle and on my computer. here is the new edited sample that works:
https://jsfiddle.net/Lhr0d6cw/14/
setTimeout(() => {
this.classList.add("open");
}, 20);
I would still like to know if there is a better way of doing this animation if someone would like to share!

why the image doesn't take the full height of the page?

I want the image (any image) height equal the window height even when I resize, I want to do it by jquery.
I used resize method in jquery but I don't get the results I need.
html:
<div class="header">
<div class="container">
</div>
</div>
css:
.container {
width: 1200px;
margin: auto;
}
.header {
background: url('https://preview.ibb.co/cu9YyH/download.jpg');
background-repeat: no-repeat;
}
jquery:
$(function () {
$(".header").height($(window).height());
$(window).resize(function () {
$(".header").height($(window).height());
});
});
There's no need for JS here. CSS alone will do the job, and is preferable for two reasons. Firstly this is a UI concern, so you shouldn't use JS as a crutch for that. Secondly, it performs better and you don't need to rely on the resize event to update the settings on the element.
To achieve what you need use vh (viewport height) units, like this:
html, body {
padding: 0;
margin: 0;
}
header {
background-color: #ccc;
height: 100vh;
}
div {
min-height: 50px;
}
<header>
I am the full-height header...
</header>
<div>
Some content here...
</div>

Setting a length (height or width) for one element minus the variable length of another, i.e. calc(x - y), where y is unknown

I know we can use calc when lengths are defined:
flex-basis: calc(33.33% - 60px);
left: calc(50% - 25px);
height: calc(100em/5);
But what if a length is variable?
height: calc(100% - <<header with variable height>>);
OR
width: calc(100% - 50px - <<box with variable width>>);
Is there a standard way to do this in CSS?
I know the overall task is possible with flexbox and tables, but I'm wondering if CSS offers a simpler method. Flexbox, tables and simple Javascript are acceptable alternatives.
height demo
width demo
You can use CSS tables:
.wrapper {
display: table;
width: 100%;
margin: 15px 0;
}
.horizontal.wrapper > div {
display: table-cell;
white-space: nowrap; /* Prevent line wrapping */
border: 1px solid;
}
.left { width: 100px } /* Minimum width of 100px */
.center { width: 0; } /* Width given by contents */
.vertical.wrapper { height: 200px; }
.vertical.wrapper > div {
display: table-row;
}
.vertical.wrapper > div > span {
display: table-cell;
border: 1px solid;
}
.top { height: 100px; } /* Minimum heigth of 100px */
.middle { height: 0; } /* Height given by content */
.bottom { height: 100%; } /* As tall as possible */
<div class="horizontal wrapper">
<div class="left">100px wide</div>
<div class="center">Auto width, given by contents</div>
<div class="right">Remaining space</div>
</div>
<div class="vertical wrapper">
<div class="top"><span>100px tall</span></div>
<div class="middle"><span>Auto height, given by contents</span></div>
<div class="bottom"><span>Remaining space</span></div>
</div>
The horizontal case can also be achieved with floats:
#wrapper, .right { overflow: hidden; } /* Establish BFC */
#wrapper > div { border: 1px solid; }
.left, .middle { float: left; }
.left { width: 100px }
<div id="wrapper">
<div class="left">100px</div>
<div class="middle">Auto width, given by contents</div>
<div class="right">Remaining space</div>
</div>
Flexbox can do that.
Support is IE10 and up.
JSfiddle Demo
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
}
#container {
height: 100%;
display: flex;
flex-direction: column;
}
#top {
background-color: lightgreen;
}
#bottom {
background-color: lightblue;
flex: 1;
}
<div id="container">
<div id="top">green box variable height</div>
<div id="bottom">blue box no longer overflows browser window</div>
</div>
I'm looking for something simple and portable. In the same way a CSS
property can be easily applied across documents, I'm looking for
something similar in terms of ease-of-application for this function.
... isolated fix is preferred.
Horizontal:
This can be achieved using CSS only. As you do not prefer a flex layout solution, the next best bet would be a table layout.
A simple CSS snippet which you could drop into your project (and be done with) would look like this:
div.flexh {
display: table; box-sizing: border-box; padding: 0; margin: 0;
}
div.flexh > div {
display: table-cell; width: auto;
box-sizing: border-box; vertical-align: middle;
}
div.flexh > div:first-child {
/* Override your custom styling below */
min-width: 75px; width: 75px; max-width: 75px;
}
div.flexh > div:last-child { width: 100%; }
You can then add your site-specific styling to this base CSS as per site requirements. Like, nowrap etc.
Two apparent advantages of this solution are:
You do not need to change your markup and also do not need to decorate all children with classes. Just apply the class flexh to your parent div and that would be it.
Minimal Markup Required:
<div class="flexh">
<div>...</div>
<div>...</div>
<div>...</div>
</div>
You are not limited to just three columns. You could have as many columns as need be. The first one will have fixed width, the last one will be flexible, and all the columns in-between would get content-based widths.
Demo Fiddle: http://jsfiddle.net/abhitalks/qqq4mq23/
Demo Snippet:
div.flexh {
display: table; box-sizing: border-box; padding: 0; margin: 0;
/* Override your custom styling below */
width: 80%; border: 2px solid black;
border-right: 2px dashed black;
font-size: 1em;
}
div.flexh > div {
display: table-cell; width: auto;
box-sizing: border-box; vertical-align: middle;
/* Override your custom styling below */
background-color: lightgreen; border: 1px solid #ddd;
padding: 15px 5px;
}
div.flexh > div:first-child {
/* Override your custom styling below */
min-width: 75px; width: 75px; max-width: 75px;
background-color: orange;
}
div.flexh > div:last-child {
width: 100%;
/* Override your custom styling below */
background: skyblue;
}
<div class="flexh">
<div>75px Fixed Width</div>
<div>Variable Content Width</div>
<div>Flexible Remaining Width</div>
</div>
<hr/>
<div class="flexh">
<div>75px Fixed Width</div>
<div><img src='//placehold.it/128x48/66c' /></div>
<div>Flexible Remaining Width</div>
</div>
<hr/>
<div class="flexh">
<div>75px Fixed Width</div>
<div>Variable TextWidth</div>
<div>
<img src='//placehold.it/128x48/66c' />
<p>Variable ContentWidth</p>
</div>
<div>Flexible Remaining Width</div>
</div>
Vertical:
This is a bit tricky to achieve without flex layout. A table layout would not work here mainly because, the table-row would not keep a fixed height as required by your use-case. The height on a table-row or table-cell is only an indicative of the minimum height required. If the space is constrained, or the content exceeds the available space, then the cell or row will increase its height depending on the content.
As per the specs here: http://www.w3.org/TR/CSS21/tables.html#height-layout
The height of a 'table-row' element's box is calculated once the user
agent has all the cells in the row available: it is the maximum of the
row's computed 'height', the computed 'height' of each cell in the
row, and the minimum height (MIN) required by the cells...
...the height of a cell box is the minimum height required by the
content
This effect can be seen here: http://jsfiddle.net/abhitalks/6eropud3/
(Resize the window pane and you will see that the first row will increase in height as the content cannot be fit into the specified height, hence defeating the purpose)
Therefore, you can restrict the height indirectly either using inner markup like a div element, or let go of the table-layout and calculate the height for the flexible one. In your use-case, you prefer not to change the markup, hence I am not proposing an inner markup.
The best-bet here would be to use the time-tested model of plain block-level divs with the height of the flexible one to be calculated. As you have already discovered that it is not possible with CSS, you will need a small JavaScript snippet to do that for you.
A simple JavaScript snippet (no jQuery) which you could wrap in a window.load and drop into your project (and be done with) would look like this:
var flexv = document.querySelectorAll('div.flexv');
/* iterate the instances on your page */
[].forEach.call(flexv, function(div) {
var children = [].slice.call(div.children), // get all children
flexChild = children.splice(-1, 1), // get the last child
usedHeight = 0, totalHeight = div.offsetHeight;
children.forEach(function(elem) {
usedHeight += elem.offsetHeight; // aggregate the height
});
/* assign the calculated height on the last child */
flexChild[0].style.height = (totalHeight - usedHeight) + 'px';
});
The CSS snippet is more or less like the horizontal one, sans table layout, which also you could just drop into your project and just add the additional site-specific styling. Minimal markup required remains the same.
Demo Fiddle 2: http://jsfiddle.net/abhitalks/Ltcuxdwf/
Demo Snippet:
document.addEventListener("load", flexit);
function flexit(e) {
var flexv = document.querySelectorAll('div.flexv');
[].forEach.call(flexv, function(div) {
var children = [].slice.call(div.children),
flexChild = children.splice(-1, 1),
usedHeight = 0, totalHeight = div.offsetHeight;
children.forEach(function(elem) {
usedHeight += elem.offsetHeight;
});
flexChild[0].style.height = (totalHeight - usedHeight) + 'px';
});
}
div.flexv {
display: inline-table; box-sizing: border-box; padding: 0; margin: 0;
overflow: hidden;
/* Override your custom styling below */
height: 320px; width: 20%; border: 1px solid black; font-size: 1em;
margin: 8px;
}
div.flexv > div {
display: block; height: auto; box-sizing: border-box;
overflow: hidden;
/* Override your custom styling below */
background-color: lightgreen; border: 1px solid #ddd;
padding: 5px 15px;
}
div.flexv > div:first-child {
/* Override your custom styling below */
min-height: 36px; height: 36px; max-height: 36px;
background-color: orange;
}
div.flexv > div:last-child {
height: 100%;
/* Override your custom styling below */
background: skyblue;
}
<div class="flexv">
<div>36px Fixed Height</div>
<div>Variable Content Height</div>
<div>Flexible Remaining Height</div>
</div>
<div class="flexv">
<div>36px Fixed Height</div>
<div><img src='//placehold.it/64x72/66c' /></div>
<div>Flexible Remaining Height</div>
</div>
<div class="flexv">
<div>36px Fixed Height</div>
<div>Variable Text Height</div>
<div>
<img src='//placehold.it/72x48/66c' />
<p>Variable Content Height</p>
</div>
<div>Flexible Remaining Height</div>
</div>
Note: As pointed out by #LGSon, the display: inline-table used for the demo does not play well with Firefox. This is only for a demo and should be replaced by either block or inline-block as per your use-case.
Updated
As I commented earlier, and besides flex, this is also solvable using display: table and here is a fiddle demo I made showing that.
If a fixed top also were required for the vertical demo, here is an update of my original display:table version: fiddle demo
Sometimes I haven't been able (or didn't want) to use either flex nor tables, and I have, on and off, looked into making use of css calc() and css attr().
Both come short though, as calc() can only use +-*/ and attr() can only return a string value, which can't be computed by calc().
My suggestion, using plain javascript, is based on that these 2 methods, at some point, might be extended so we can make better use of them.
This is how I would like see them work;
width: calc(100% - attr(this.style.left))
but as they don't, and I can't add it to my css either as it wouldn't validate properly (might even break the parsing, who knows) I added a variant as an attribute on the element instead, with some quirks to make it easier to compute.
And in this case (the 2 demos) it looks like this:
//height
<div id="bottom" data-calcattr="top,height,calc(100% - toppx)">...</div>
//width
<div class="box right" data-calcattr="left,width,calc(100% - leftpx)">...</div>
Together with below script, which by no means is fully developed/tested on all property combinations, it does adjust the div's size.
In short, when runned, it take the attribute, split it into an array, take the first item value as from which property to read, the second to which property to set and the third to which the read value gets inserted/replaced and assigned to the property to be set (hmmm, still working on a better way to express this, but hopefully the script is clear enough with whats going on).
Here is a fiddle showing both the height and width demo, integrated, making use of the same script.
function calcattr() {
var els = document.querySelectorAll('[data-calcattr]');
for (i = 0; i < els.length; i++) {
var what = els[i].getAttribute('data-calcattr');
if (what) {
what = what.split(',');
var rect = els[i].getBoundingClientRect();
var parentrect = els[i].parentNode.getBoundingClientRect();
var brd = window.getComputedStyle(els[i].parentNode,null).getPropertyValue('border-' + what[0] + '-width');
what[2] = what[2].replace(what[0],parseInt(rect[what[0]]-parentrect[what[0]]) - parseInt(brd));
els[i].setAttribute("style", what[1] + ":" + what[2]);
}
}
}
IN CSS
Although I've never tried it, I believe that this would work:
.top {
height:13px;
}
.main {
height:calc(100% - var(height));
}
http://www.creativebloq.com/netmag/why-you-need-use-css-variables-91412904
IN SASS
$top_height: 50px
.main {
height: calc(100% - $top_height)
}
Sass Variable in CSS calc() function
In both cases on container css you should put:
#container {
overflow: hidden;
}
But, it will hide the information that overflows the container. I think that is the point, since you put white-space: nowrap; it means that you don't want to change the height, so you have to hide the text that can't fits the container.

ReCaptcha API v2 Styling

I have not had much success finding how to style Google's new recaptcha (v2). The eventual goal is to make it responsive, but I am having difficulty applying styling for even simple things like width.
Their API documentation does not appear to give any specifics on how to control styling at all other than the theme parameter, and simple CSS & JavaScript solutions haven't worked for me.
Basically, I need to be able to apply CSS to Google's new version of reCaptcha. Using JavaScript with it is acceptable.
Overview:
Sorry to be the answerer of bad news, but after research and debugging, it's pretty clear that there is no way to customize the styling of the new reCAPTCHA controls. The controls are wrapped in an iframe, which prevents the use of CSS to style them, and Same-Origin Policy prevents JavaScript from accessing the contents, ruling out even a hacky solution.
Why No Customize API?:
Unlike reCAPTCHA API Version 1.0, there are no customize options in API Version 2.0. If we consider how this new API works, it's no surprise why.
Excerpt from Are you a robot? Introducing “No CAPTCHA reCAPTCHA”:
While the new reCAPTCHA API may sound simple, there is a high degree of sophistication behind that modest checkbox. CAPTCHAs have long relied on the inability of robots to solve distorted text. However, our research recently showed that today’s Artificial Intelligence technology can solve even the most difficult variant of distorted text at 99.8% accuracy. Thus distorted text, on its own, is no longer a dependable test.
To counter this, last year we developed an Advanced Risk Analysis backend for reCAPTCHA that actively considers a user’s entire engagement with the CAPTCHA—before, during, and after—to determine whether that user is a human. This enables us to rely less on typing distorted text and, in turn, offer a better experience for users. We talked about this in our Valentine’s Day post earlier this year.
If you were able to directly manipulate the styling of the control elements, you could easily interfere with the user-profiling logic that makes the new reCAPTCHA possible.
What About a Custom Theme?:
Now the new API does offer a theme option, by which you can choose a preset theme such as light and dark. However there is not presently a way to create a custom theme. If we inspect the iframe, we will find the theme name is passed in the query string of the src attribute. This URL looks something like the following.
https://www.google.com/recaptcha/api2/anchor?...&theme=dark&...
This parameter determines what CSS class name is used on the wrapper element in the iframe and determines the preset theme to use.
Digging through the minified source, I found that there are actually 4 valid theme values, which is more than the 2 listed in the documentation, but default and standard are the same as light.
We can see the code that selects the class name from this object here.
There is no code for a custom theme, and if any other theme value is specified, it will use the standard theme.
In Conclusion:
At present, there is no way to fully style the new reCAPTCHA elements, only the wrapper elements around the iframe can be stylized. This was almost-certainly done intentionally, to prevent users from breaking the user profiling logic that makes the new captcha-free checkbox possible. It is possible that Google could implement a limited custom theme API, perhaps allowing you to choose custom colors for existing elements, but I would not expect Google to implement full CSS styling.
As guys mentioned above, there is no way ATM. but still if anyone interested, then by adding in just two lines you can at least make it look reasonable, if it break on any screen. you can assign different value in #media query.
<div id="recaptchaContainer" style="transform:scale(0.8);transform-origin:0 0"></div>
Hope this helps anyone :-).
I use below trick to make it responsive and remove borders. this tricks maybe hide recaptcha message/error.
This style is for rtl lang but you can change it easy.
.g-recaptcha {
position: relative;
width: 100%;
background: #f9f9f9;
overflow: hidden;
}
.g-recaptcha > * {
float: right;
right: 0;
margin: -2px -2px -10px;/*remove borders*/
}
.g-recaptcha::after{
display: block;
content: "";
position: absolute;
left:0;
right:150px;
top: 0;
bottom:0;
background-color: #f9f9f9;
clear: both;
}
<div class="g-recaptcha" data-sitekey="Your Api Key"></div>
<script src='https://www.google.com/recaptcha/api.js?hl=fa'></script>
Unfortunately we cant style reCaptcha v2, but it is possible to make it look better, here is the code:
Click here to preview
.g-recaptcha-outer{
text-align: center;
border-radius: 2px;
background: #f9f9f9;
border-style: solid;
border-color: #37474f;
border-width: 1px;
border-bottom-width: 2px;
}
.g-recaptcha-inner{
width: 154px;
height: 82px;
overflow: hidden;
margin: 0 auto;
}
.g-recaptcha{
position:relative;
left: -2px;
top: -1px;
}
<div class="g-recaptcha-outer">
<div class="g-recaptcha-inner">
<div class="g-recaptcha" data-size="compact" data-sitekey="YOUR KEY"></div>
</div>
</div>
Add a data-size property to the google recaptcha element and make it equal to "compact" in case of mobile.
Refer: google recaptcha docs
What you can do is to hide the ReCaptcha Control behind a div. Then make your styling on this div. And set the css "pointer-events: none" on it, so you can click through the div (Click through a DIV to underlying elements).
The checkbox should be in a place where the user is clicking.
You can recreate recaptcha , wrap it in a container and only let the checkbox visible. My main problem was that I couldn't take the full width so now it expands to the container width. The only problem is the expiration you can see a flick but as soon it happens I reset it.
See this demo http://codepen.io/alejandrolechuga/pen/YpmOJX
function recaptchaReady () {
grecaptcha.render('myrecaptcha', {
'sitekey': '6Lc7JBAUAAAAANrF3CJaIjt7T9IEFSmd85Qpc4gj',
'expired-callback': function () {
grecaptcha.reset();
console.log('recatpcha');
}
});
}
.recaptcha-wrapper {
height: 70px;
overflow: hidden;
background-color: #F9F9F9;
border-radius: 3px;
box-shadow: 0px 0px 4px 1px rgba(0,0,0,0.08);
-webkit-box-shadow: 0px 0px 4px 1px rgba(0,0,0,0.08);
-moz-box-shadow: 0px 0px 4px 1px rgba(0,0,0,0.08);
height: 70px;
position: relative;
margin-top: 17px;
border: 1px solid #d3d3d3;
color: #000;
}
.recaptcha-info {
background-size: 32px;
height: 32px;
margin: 0 13px 0 13px;
position: absolute;
right: 8px;
top: 9px;
width: 32px;
background-image: url(https://www.gstatic.com/recaptcha/api2/logo_48.png);
background-repeat: no-repeat;
}
.rc-anchor-logo-text {
color: #9b9b9b;
cursor: default;
font-family: Roboto,helvetica,arial,sans-serif;
font-size: 10px;
font-weight: 400;
line-height: 10px;
margin-top: 5px;
text-align: center;
position: absolute;
right: 10px;
top: 37px;
}
.rc-anchor-checkbox-label {
font-family: Roboto,helvetica,arial,sans-serif;
font-size: 14px;
font-weight: 400;
line-height: 17px;
left: 50px;
top: 26px;
position: absolute;
color: black;
}
.rc-anchor .rc-anchor-normal .rc-anchor-light {
border: none;
}
.rc-anchor-pt {
color: #9b9b9b;
font-family: Roboto,helvetica,arial,sans-serif;
font-size: 8px;
font-weight: 400;
right: 10px;
top: 53px;
position: absolute;
a:link {
color: #9b9b9b;
text-decoration: none;
}
}
g-recaptcha {
// transform:scale(0.95);
// -webkit-transform:scale(0.95);
// transform-origin:0 0;
// -webkit-transform-origin:0 0;
}
.g-recaptcha {
width: 41px;
/* border: 1px solid red; */
height: 38px;
overflow: hidden;
float: left;
margin-top: 16px;
margin-left: 6px;
> div {
width: 46px;
height: 30px;
background-color: #F9F9F9;
overflow: hidden;
border: 1px solid red;
transform: translate3d(-8px, -19px, 0px);
}
div {
border: 0;
}
}
<script src='https://www.google.com/recaptcha/api.js?onload=recaptchaReady&&render=explicit'></script>
<div class="recaptcha-wrapper">
<div id="myrecaptcha" class="g-recaptcha"></div>
<div class="rc-anchor-checkbox-label">I'm not a Robot.</div>
<div class="recaptcha-info"></div>
<div class="rc-anchor-logo-text">reCAPTCHA</div>
<div class="rc-anchor-pt">
Privacy
<span aria-hidden="true" role="presentation"> - </span>
Terms
</div>
</div>
Great!
Now here is styling available for reCaptcha..
I just use inline styling like:
<div class="g-recaptcha" data-sitekey="XXXXXXXXXXXXXXX" style="transform: scale(1.08); margin-left: 14px;"></div>
whatever you wanna to do small customize in inline styling...
Hope it will help you!!
I came across this answer trying to style the ReCaptcha v2 for a site that has a light and a dark mode. Played around some more and discovered that besides transform, filter is also applied to iframe elements so ended up using the default/light ReCaptcha and doing this when the user is in dark mode:
.g-recaptcha {
filter: invert(1) hue-rotate(180deg);
}
The hue-rotate(180deg) makes it so that the logo is still blue and the check-mark is still green when the user clicks it, while keeping white invert()'ed to black and vice versa.
Didn't see this in any answer or comment so decided to share even if this is an old thread.
Just adding a hack-ish solution to make it responsive.
Wrap the recaptcha in an extra div:
<div class="recaptcha-wrap">
<div id="g-recaptcha"></div>
</div>
Add styles. This assumes the dark theme.
// Recaptcha
.recaptcha-wrap {
position: relative;
height: 76px;
padding:1px 0 0 1px;
background:#222;
> div {
position: absolute;
bottom: 2px;
right:2px;
font-size:10px;
color:#ccc;
}
}
// Hides top border
.recaptcha-wrap:after {
content:'';
display: block;
background-color: #222;
height: 2px;
width: 100%;
top: -1px;
left: 0px;
position: absolute;
}
// Hides left border
.recaptcha-wrap:before {
content:'';
display: block;
background-color: #222;
height: 100%;
width: 2px;
top: 0;
left: -1px;
position: absolute;
z-index: 1;
}
// Makes it responsive & hides cut-off elements
#g-recaptcha {
overflow: hidden;
height: 76px;
border-right: 60px solid #222222;
border-top: 1px solid #222222;
border-bottom: 1px solid #222;
position: relative;
box-sizing: border-box;
max-width: 294px;
}
This yields the following:
It will now resize horizontally, and doesn't have a border. The recaptcha logo would get cut off on the right, so I am hiding it with a border-right. It's also hiding the privacy and terms links, so you may want to add those back in.
I attempted to set a height on the wrapper element, and then vertically center the recaptcha to reduce the height. Unfortunately, any combo of overflow:hidden and a smaller height seems to kill the iframe.
in the V2.0 it's not possible. The iframe blocks all styling out of this. It's difficult to add a custom theme instead of the dark or light one.
Late to the party, but maybe my solution will help somebody.
I haven't found any solution that works on a responsive website when the viewport changes or the layout is fluid.
So I've created a jQuery script for django-cms that is dynamically adapting to a changing viewport.
I'm going to update this response as soon as I have the need for a modern variant of it that is more modular and has no jQuery dependency.
html
<div class="g-recaptcha" data-sitekey="{site_key}" data-size={size}>
</div>
css
.g-recaptcha { display: none; }
.g-recaptcha.g-recaptcha-initted {
display: block;
overflow: hidden;
}
.g-recaptcha.g-recaptcha-initted > * {
transform-origin: top left;
}
js
window.djangoReCaptcha = {
list: [],
setup: function() {
$('.g-recaptcha').each(function() {
var $container = $(this);
var config = $container.data();
djangoReCaptcha.init($container, config);
});
$(window).on('resize orientationchange', function() {
$(djangoReCaptcha.list).each(function(idx, el) {
djangoReCaptcha.resize.apply(null, el);
});
});
},
resize: function($container, captchaSize) {
scaleFactor = ($container.width() / captchaSize.w);
$container.find('> *').css({
transform: 'scale(' + scaleFactor + ')',
height: (captchaSize.h * scaleFactor) + 'px'
});
},
init: function($container, config) {
grecaptcha.render($container.get(0), config);
var captchaSize, scaleFactor;
var $iframe = $container.find('iframe').eq(0);
$iframe.on('load', function() {
$container.addClass('g-recaptcha-initted');
captchaSize = captchaSize || { w: $iframe.width() - 2, h: $iframe.height() };
djangoReCaptcha.resize($container, captchaSize);
djangoReCaptcha.list.push([$container, captchaSize]);
});
},
lateInit: function(config) {
var $container = $('.g-recaptcha.g-recaptcha-late').eq(0).removeClass('.g-recaptcha-late');
djangoReCaptcha.init($container, config);
}
};
window.djangoReCaptchaSetup = window.djangoReCaptcha.setup;
With the integration of the invisible reCAPTCHA you can do the following:
To enable the Invisible reCAPTCHA, rather than put the parameters in a div, you can add them directly to an html button.
a. data-callback=””. This works just like the checkbox captcha, but is required for invisible.
b. data-badge: This allows you to reposition the reCAPTCHA badge (i.e. logo and
‘protected by reCAPTCHA’ text) . Valid options as ‘bottomright’ (the default),
‘bottomleft’ or ‘inline’ which will put the badge directly above the button. If you
make the badge inline, you can control the CSS of the badge directly.
In case someone struggling with the recaptcha of contact form 7 (wordpress) here is a solution working for me
.wpcf7-recaptcha{
clear: both;
float: left;
}
.wpcf7-recaptcha{
margin-right: 6px;
width: 206px;
height: 65px;
overflow: hidden;
border-right: 1px solid #D3D3D3;
}
.wpcf7-recaptcha iframe{
padding-bottom: 15px;
border-bottom: 1px solid #D3D3D3;
background: #F9F9F9;
border-left: 1px solid #d3d3d3;
}
if you use scss, that worked for me:
.recaptcha > div{
transform: scale(0.84);
transform-origin: 0;
}
If someone is still interested, there is a simple javascript library (no jQuery dependency), named custom recaptcha. It lets you customize the button with css and implement some js events (ready/checked). The idea is to make the default recaptcha "invisible" and put a button over it. Just change the id of the recaptcha and that's it.
<head>
<script src="https://azentreprise.org/download/custom-recaptcha.min.js"></script>
<style type="text/css">
#captcha {
float: left;
margin: 2%;
background-color: rgba(72, 61, 139, 0.5); /* darkslateblue with 50% opacity */
border-radius: 2px;
font-size: 1em;
color: #C0FFEE;
}
#captcha.success {
background-color: rgba(50, 205, 50, 0.5); /* limegreen with 50% opacity */
color: limegreen;
}
</style>
</head>
<body>
<div id="captcha" data-sitekey="your_site_key" data-label="Click here" data-label-spacing="15"></div>
</body>
See https://azentreprise.org/read.php?id=1 for more information.
I am just adding this kind of solution / quick fix so it won't get lost in case of a broken link.
Link to this solution "Want to add link How to resize the Google noCAPTCHA reCAPTCHA | The Geek Goddess" was provided by Vikram Singh Saini and simply outlines that you could use inline CSS to enforce framing of the iframe.
// Scale the frame using inline CSS
<div class="g-recaptcha" data-theme="light"
data-sitekey="XXXXXXXXXXXXX"
style="transform:scale(0.77);
-webkit-transform:scale(0.77);
transform-origin:0 0;
-webkit-transform-origin:0 0;
">
</div>
// Scale the images using a stylesheet
<style>
#rc-imageselect, .g-recaptcha {
transform:scale(0.77);
-webkit-transform:scale(0.77);
transform-origin:0 0;
-webkit-transform-origin:0 0;
}
</style>
You can use some CSS for Google reCAPTCHA v2 styling on your website:
– Change background, color of Google reCAPTCHA v2 widget:
.rc-anchor-light {
background: #fff!important;
color: #fff!important; }
or
.rc-anchor-normal{
background: #000 !important;
color: #000 !important; }
– Resize the Google reCAPTCHA v2 widget by using this snippet:
.rc-anchor-light {
transform:scale(0.9);
-webkit-transform:scale(0.9); }
– Responsive your Google reCAPTCHA v2:
#media only screen and (min-width: 768px) {
.rc-anchor-light {
transform:scale(0.85);
-webkit-transform:scale(0.85); }
}
All elements, property of CSS above that’s just for your reference. You can change them by yourself (only using CSS class selector).
Refer on OIW Blog - How To Edit CSS of Google reCAPTCHA (Re-style, Change Position, Resize reCAPTCHA Badge)
You can also find out Google reCAPTCHA v3's styling there.
A bit late but I tried this and it worked to make the Recaptcha responsive on screens smaller than 460px width. You can't use css selector to select elements inside the iframe. So, better use the outermost parent element which is the class g-recaptcha to basically zoom-out i.e transform the size of the entire container. Here's my code which worked:
#media(max-width:459.99px) {
.modal .g-recaptcha {
transform:scale(0.75);
-webkit-transform:scale(0.75); }
}
}
Incase someone wants to resize recaptcha for small devices.
I was using recaptcha V2 with primeng p-captcha (for angular). The issue was that for smaller screens it would go out of the screen.
Although you can't actually resize it (the external thing and all everyone has explained it above) but there is a way with transform property (scaling the the container)
this was my code below the way, I achieved it
p-captcha div div {
transform:scale(0.9) !important;
-webkit-transform:scale(0.9) !important;
transform-origin:0 0 !important;
-webkit-transform-origin:0 0 !important;
}
Other than p-captcha you can use this code snippet below
.g-recaptcha {
transform:scale(0.9);
transform-origin:0 0;
}
Before
After
Topic is old, but I also wanted to scale the reCAPTCHA widget -- but to make it bigger for phone users, unlike many others who wanted it smaller. The only way that worked was transform: scale(x), but that seemed to make the widget too wide for my page, thus shrinking the rest of the form on the page. Using a container div as shown below fixed my problem, and hopefully it will help someone else who thinks a bigger version is better on a small screen.
<style>
:root {
/* factor to scale the Google widget in potrait mode (on a phone) */
--recaptcha-scale: 2;
}
#media screen and (orientation: portrait) {
/* needed to rein in the width of inner div when it is scaled */
#g_recaptcha_div_container {
width: calc(100vmin / var(--recaptcha-scale));
}
#g_recaptcha_div {
transform: scale(var(--recaptcha-scale));
transform-origin: 0 0;
}
#submit_button {
width: 65vmin;
height: 9vmin;
font-size: 7vmin;
/* needed to scoot the button out from under the scaled div */
margin-top: 10vmin;
}
}
</style>
<html>
<!-- top of form with a bunch of fields to create an acct -->
<div id="g_recaptcha_div_container">
<div id="g_recaptcha_div" class="g-recaptcha" data-sitekey="foo">
</div>
</div>
<input id="submit_button" type="submit" value="Create Account">
<!-- bottom of form -->
</html>
You can try to color it with this css filter hack:
.colorize-pink {
filter: brightness(0.5) sepia(1) hue-rotate(-70deg) saturate(5);
}
.colorize-navy {
filter: brightness(0.2) sepia(1) hue-rotate(180deg) saturate(5);
}
and for the size, use transform css hack
.captcha-size {
transform:scale(0.8);transform-origin:0 0
}
Lets play a little with JavaScript:
First at all, we know that recaptcha badget include all the shit from the most crazy people on Google, so you can only make changes with theme "dark" and "light" on your web.
Take a look to my website
SantiagoSoñora.
let recaptcha = document.querySelector('.g-recaptcha');
With this, you only can touch simple settings of the badge, like z-index and size, but no much more...
So far, i made two functions that set data-theme to light or dark mode at innit. Note that its neccessary assign the "light" because Google not include that by default.
function reCaptchaDark() {
document.addEventListener('DOMContentLoaded', (event) => {
recaptcha.setAttribute("data-theme", "dark");
})
}
function reCaptchaLight() {
document.addEventListener('DOMContentLoaded', (event) => {
recaptcha.setAttribute("data-theme", "light");
})
}
Then, for example, my web looks if user prefers a dark or a light theme, and set that configurations to the recaptcha bag:
(theme.onLoad = function() {
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
reCaptchaDark();
toggleTheme();
}
else {
reCaptchaLight();
}
})();
Note that my code for toggle from dark to light is on the toggleTheme() function.
Keep doing magic: You should configure a class on the html tag or something else on your web for made the change between dark and light theme, and with that we now modify the src on the iframe so when we toggle dark/light mode ,with our button it changes:
theme.onclick = function() {
toggleTheme();
if (html.classList.contains('dark')) {
recaptcha.setAttribute("data-theme", "dark");
setTimeout(function() {
let iframes = document.querySelectorAll('iframe');
iframes[0].src = iframes[0].src.replace('&theme=light', '&theme=dark');
}, 0);
}
else {
recaptcha.setAttribute("data-theme", "light");
setTimeout(function() {
let iframes = document.querySelectorAll('iframe');
iframes[0].src = iframes[0].src.replace('&theme=dark', '&theme=light');
}, 0);
}
}
And here you go, the recaptcha badge change from dark to light "preassigned" themes by Google bad guys.
And last but not least, a function that updates the page to change if your theme is dark by default.
This update the LocalStorage
(function() {
if( window.localStorage ) {
if( !localStorage.getItem('firstLoad') ) {
localStorage['firstLoad'] = true;
window.location.reload();
}
else
localStorage.removeItem('firstLoad');
}
})();
You can use the class .grecaptcha-badge for some css changes, like opacity and box-shadow, -> (use !important)
Thats all, hope you can implement on your site

Position badge over corner of image automatically

I have a layout where images "float" within a certain area. The layout looks like this:
The source like this:
<div class="free_tile">
<a class="img_container canonical" href="/photos/10">
<img class="canonical" src="http://s3.amazonaws.com/t4e-development/photos/1/10/andrew_burleson_10_tile.jpg?1303238025" alt="Andrew_burleson_10_tile">
<!-- EDIT: I am aware that I can put the badge here. See the edit notes and image below. -->
</a>
<div class="location">Houston</div>
<div class="taxonomy"> T6 | Conduit | Infrastructure </div>
</div>
The CSS looks like this (in SCSS):
div.free_tile { width: 176px; height: 206px; float: left; margin: 0 20px 20px 0; position: relative;
&.last { margin: 0 0 20px 0; }
a.img_container { display: block; width: 176px; height: 158px; text-align: center; line-height: 156px; margin-bottom: 10px; }
img { margin: 0; border: 1px solid $dark3; display: inline-block; vertical-align: middle; #include boxShadow;
&.canonical { border: 1px solid $transect; }
}
.location, .taxonomy { width: 176px; }
.location { font-weight: 700; }
.taxonomy { line-height: 10px; font-size: 10px; text-transform: uppercase; height: 20px; overflow: hidden; }
}
div.transect_badge { height: 20px; width: 20px; background: url('/images/transect-badge.png'); }
So, basically the images are sitting vertically-aligned middle and text-aligned center, and they have a maximum width of 176 and max height of 158, but they're cropped to maintain the original aspect ratio so the actual top corner of each image falls differently depending on which image it is.
I have a badge that I'd like to put in the top corner of certain images (when the image is "canonical"). You see the style for this above (div.transect_badge).
The problem, of course, is I don't know where the top corner of the image will be so I can't hardcode the position via CSS.
I assume that I'll need to do this via jQuery or something. So, I started with a jQuery method to automatically append the badge div to any canonical images. That works fine, but I can't figure out how to position it over the top left corner.
How can this be done? (ideally using just HTML and CSS, but realistically using JS/jQuery)
--EDIT--
Here's the problem: The image is floating inside a container, so the corner of the image might fall anywhere inside the outer limits of the container. Here's an example of what happens if I try to use position:absolute; top:0; left:0 inside the same container the image is bound by:
It took some tryouts, but here it is: the size independent image badge positioner.
HTML:
<div class="tile">
<span class="photo">
<img src="/photos/10.jpg" alt="10" /><ins></ins>
</span>
<p class="location">Houston</p>
<p class="taxonomy">T6 | Conduit | Infrastructure</p>
</div>
CSS:
.tile {
float: left;
width: 176px;
height: 206px;
margin: 0 20px 20px 0;
}
.photo {
display: block;
width: 176px;
height: 158px;
text-align: center;
line-height: 158px;
margin-bottom: 10px;
}
a {
display: inline-block;
position: relative;
line-height: 0;
}
img {
border: none;
vertical-align: middle;
}
ins {
background: url('/images/badge.png') no-repeat 0 0;
position: absolute;
left: 0;
top: 0;
width: 20px;
height: 20px;
}
Example:
In previous less successful attempts (see edit history), the problem was getting the image vertically centered ánd to get its parent the same size (in order to position the badge in the top-left of that parent). As inline element that parent doesn't care about the height of its contents and thus remains to small, but as block element it stretches to hís parent's size and thus got to high, see demonstration fiddle. The trick seems to be to give that parent a very small line-height (e.g. 0) and display it as an inline-block. That way the parent will grow according to its childs.
Tested in Opera 11, Chrome 11, IE8, IE9, FF4 and Safari 5 with all DTD's. IE7 fails, but a center-top alignment of the photo with badge at the right position isn't that bad at all. Works also for IE7 now because I deleted the spaces in the markup within the a tag. Haha, how weird!
EDIT3: This solution is very similar to my original solution. I didn't really look at your code much so I should have noticed this earlier. Your a tag is already wrapping each image so you can just add the badge in there and position it absolute. The a tag doesn't need width/height. Also you must add the badge image at the beginning of your a tag.
Demo: http://jsfiddle.net/wdm954/czxj2/1/
div.free_tile {
width: 176px;
height: 206px;
float: left;
}
a.img_container {
display: block;
margin-bottom: 10px;
}
span.transect_badge {
display:block;
position: absolute;
height: 20px;
width: 20px;
background-image: url('/images/transect-badge.png');
}
HTML...
<a class="img_container canonical" href="/photos/10">
<span class="transect_badge"></span>
<img class="canonical" src="path/to/img" />
</a>
Other solutions...
In my code I'm using SPAN tags so simulate images, but it's the same idea. The badge image, when positioned absolute, will create the desired effect.
Demo: http://jsfiddle.net/wdm954/62faE/
EDIT: In the case that you need jQuery to position. This should work (where .box is your container and .corner is the badge image)...
$('.box').each(function() {
$(this).find('.corner')
.css('margin-top', ( $(this).width() - $(this).find('.img').width() ) / 2);
$(this).find('.corner')
.css('margin-left', ( $(this).height() - $(this).find('.img').height() ) / 2);
});
EDIT2: Another solution would be to wrap each image with a new container. You would have to move the code that you use to center each image to the class of the new wrapping container.
Demo: http://jsfiddle.net/wdm954/62faE/1/
$('.img').wrap('<span class="imgwrap" />');
$('.imgwrap').prepend('<span class="badge" />');
Technically you can just add something like this to your HTML though without using jQuery to insert it.
Use an element other than <div>, e.g. <span> and put it inside your <a> element after the <img> element. Then, give the <a> element position:relative; and the <span> gets position:absolute; top:0px; left:0px;. That is, if you don't mind the badge also being part of the same link - but it's the easiest way. Also, the reason for using <span> is to keep your HTML4 valid, <div> would still be HTML5 valid, however.
I did find one solution using jQuery. I don't prefer this because it noticably impacts page loading, but it is acceptable if nothing else will work. I'm more interested in NGLN's idea which seems promising but I haven't entirely figured out yet. However, since this thread has picked up a lot of traffic I thought I'd post one solution that I came up with for future readers to consider:
Given this markup:
<div class="free_tile">
<a class="img_container canonical" href="/photos/10">
<img class="canonical" src="http://s3.amazonaws.com/t4e-development/photos/1/10/andrew_burleson_10_tile.jpg?1303238025" alt="Andrew_burleson_10_tile">
<span class="transect-badge"></span>
</a>
<div class="location">Houston</div>
<div class="taxonomy"> T6 | Conduit | Infrastructure </div>
</div>
Same CSS as in question except:
span.transect-badge { display: block; height: 20px; width: 20px; position: absolute; background: url('/images/transect-badge.png'); }
Then this jQuery solves the problem:
$(function() {
$('img.canonical').load( function() {
var position = $(this).position();
$(this).next().css({ 'top': position.top+1, 'left': position.left+1 });
});
});
Like I said, though, this incurs noticeable run-time on the client end, so I'd prefer to use a non JS solution if I can. I'll continue to leave this question open while I test out and give feedback on the other solutions offered, with hopes of finding one of them workable without JS.

Categories

Resources