Smart DOM Insertion (preserve styles of child element) - javascript

This is a very odd question, of which I am fairly confident I already know the answer, however, before I go ahead with the fix that I am trying to avoid, I thought it was worth asking others as you may see this from a different angle
I am currently writing a plugin that takes a certain element (can be any DOM node), and wraps it with a div. The purpose of the plugin is to add a 'blinds' effect to elements which can then be used to reveal the element on scroll.
Note: Wrapping with a div is absolutely essential to the functionality I am implementing.
My question, quite simply, is anybody aware of a smart way to preserve the styles from the element being wrapped and ensure it visually remains the same as it was before the div wrapping, and, whilst retaining the dimensions so another div can be inserted within the wrapper and be the same dimensions of it's sibling.
See this very basic example (before and after - notice the blue blind):
.test {
display: flex;
flex-flow: column;
height: 200px;
text-align: center;
}
.test:nth-child(1) {
background: #999;
}
.test:nth-child(2) {
background: #666;
}
.test > * {
flex: 0 1 auto;
}
.test h1 {
margin: auto 0 0;
color: #fff;
}
.test p {
margin: 0 0 auto;
color: #fff;
}
.test-wrapper {
position: relative;
width: max-content; /* Fix for example purposes */
margin: 0 auto; /* Fix for example purposes */
}
.test-wrapper .blind {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: blue;
opacity: 0.5;
}
<div class="test">
<h1>Before wrapper inserted</h1>
<p>Some description</p>
</div>
<div class="test">
<div class="test-wrapper">
<div class="blind"></div>
<h1>After wrapper inserted</h1>
</div>
<p>Some description</p>
</div>
Before answering, these are the solutions I am trying to avoid:
'copying' or 'moving' the styles (or certain styles) from the child to the parent as this is likely to cause a huge mess... Open to suggestions though
Defining styles for test-wrapper in the site's CSS as test-wrapper is a plugin specific element.
The obvious, and very easy, solution is to just define the styles for the wrapper instead of the child in my CSS (SCSS). I appreciate this may be the only way, but I ideally want to avoid this as what I am building is a plugin and I want the HTML/CSS to work with and without the element wrapper in place.

Related

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

Stack of slides continuously growing

Let us say I want to design a website with four slides. I would like each slide to cover the previous one while the visitor is scrolling. Following is an attempt with stellar.js (a jquery plugin): http://jsfiddle.net/8mxugjqe/. You can see that it works for the first slide, which gets covered by the second one, but I could not have it work for the others.
HTML:
<body>
<div id="one" data-stellar-ratio=".2">
<p>This is the first div.</p>
</div>
<div id="two" data-stellar-ratio="1">
<p>This is the second one.</p>
</div>
<div id="three">
<p>Third one!</p>
</div>
<div id="four">
<p>Fourth and last.</p>
</div>
</body>
CSS:
* {
margin: 0;
padding: 0;
}
#one, #two, #three, #four {
position: absolute;
height: 100%;
width: 100%;
font-size: 5em;
}
p {
margin: 1em;
width: 60%;
}
#one {
background: red;
}
#two {
background: blue;
top: 100%;
}
#three {
background: green;
top: 200%;
}
#four {
background: yellow;
top: 300%;
}
I was able to throw something together using just jQuery and no other libraries. It relies on relative positioning. Basically, everything scrolls normally until one of the slides reaches the top of the browser window. Once it tries to scroll past the top of the browser window, I add an offset to the slide's vertical position to keep it from moving up any further. When scrolling back the other way, I simply subtract from this offset until it hits 0 at which point it begins to scroll normally again.
I'm sure the code can be cleaned up but I added a ton of comments so hopefully it's readable. If you have any questions or you would like me to modify it to better suit your needs, let me know. Here's a fiddle with the solution I came up with:
http://jsfiddle.net/jwnace/jhxfe2gg/
You can also see a full page demo of the same code here:
http://joenace.com/slides/

Javascript "object" not retaining css values

I'm defining a javascript "object" via the following function:
function Window(vars) {
this.div = $("<div/>", {
id: vars.id,
class: vars.styles + " box text",
css: {
top: vars.top,
left: vars.left
}
});
this.div.appendTo( $("body") );
// more stuff happens..
As you can see, the Window has a div property, which is a jQuery object. In it's instantiation, I declare the CSS classes box and text. text is not important, it's just font stuff. Here's the CSS for box, however.
.box {
z-index: 1;
position: absolute;
background: #222222;
min-width: 10%;
text-align: center;
padding: 5px;
}
.nav-extension {
z-index: 3;
padding: 8px;
background: #000000;
position: absolute;
-moz-border-radius-bottomright: 5px;
-moz-border-radius-topright: 5px;
border-bottom-right-radius: 5px;
border-top-right-radius: 5px;
}
box is absolute at z-index 1, and another div with nav-extension is somewhere else on the page, also absolute and at z-index 3. However, when I add the Window object to my page, it appears above anything with nav-extension. All other CSS attribues, like background, still work.
I've tried altering the z-index where I instantiate the div in the "css" section I'm already using, but that didn't work either. What gives?
Edit
Also, I've inspected both the div with box and the one with nav-extension with Firefox, and the "Style" tab indicates they still have their intended z-index (not overridden).
#2: Changed vars.class to vars.styles.
Could you give us some DOM, please? It appears that that your box and the nav-extension-div are in different contexts. A non-static position sets up a new context, relatively to which all z-indexes inside are processed. A simple fiddle to demonstrate: http://jsfiddle.net/3KTyz/
<body>
<style>
.box { z-index:1; }
.nav-extension { z-index:3; }
</style>
...
<div id="context" style="position:relative"><!-- or absolute or fixed -->
...
<div class="nav-extension"><!--
will be positioned +3 relatively to other elements in #context
-->...</div>
</div>
<div class="box"><!--
will be above #context, which has (implicit) z-index:0
-->...</div>
</body>
To make nav-extension appear above the box, you will either
set #context (or one of its parents) to a z-index higher than the one of box or
move the nav-extension-div outside any context
class is a reserved word in JavaScript and cannot be used as a property or a variable name, maybe this is the culprit.

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.

How do I achieve equal height divs (positioned side by side) with HTML / CSS ?

I have two divs inside of a container. One on the left, one on the right, side by side. How am I able to make each one be of equal height, even though they have different content.
For example, the right div has a lot of content, and is double the height of the left div, how do I make the left div stretch to the same height of the right div?
Is there some JavaScript (jQuery) code to accomplish this?
You could use jQuery, but there are better ways to do this.
This sort of question comes up a lot and there are generally 3 answers...
1. Use CSS
This is the 'best' way to do it, as it is the most semantically pure approach (without resorting to JS, which has its own problems). The best way is to use the display: table-cell and related values. You could also try using the faux background technique (which you can do with CSS3 gradients).
2. Use Tables
This seems to work great, but at the expense of having an unsemantic layout. You'll also cause a stir with purists. I have all but avoided using tables, and you should too.
3. Use jQuery / JavaScript
This benefits in having the most semantic markup, except with JS disabled, you will not get the effect you desire.
Here's a way to do it with pure CSS, however, as you'll notice in the example (which works in IE 7 and Firefox), borders can be difficult - but they aren't impossible, so it all depends what you want to do. This example assumes a rather common CSS structure of body > wrapper > content container > column 1 and column 2.
The key is the bottom margin and its canceling padding.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Equal Height Columns</title>
<style type="text/css">
<!--
* { padding: 0; margin: 0; }
#wrapper { margin: 10px auto; width: 600px; }
#wrapper #main_container { width: 590px; padding: 10px 0px 10px 10px; background: #CCC; overflow: hidden; border-bottom: 10px solid #CCC; }
#wrapper #main_container div { float: left; width: 263px; background: #999; padding: 10px; margin-right: 10px; border: 1px solid #000; margin-bottom: -1000px; padding-bottom: 1000px; }
#wrapper #main_container #right_column { background: #FFF; }
-->
</style>
</head>
<body>
<div id="wrapper">
<div id="main_container">
<div id="left_column">
<p>I have two divs inside of a container. One on the left, one on the right, side by side. How am I able to make each one be of equal height, even though they have different content.</p>
</div><!-- LEFT COLUMN -->
<div id="right_column">
<p>I have two divs inside of a container. One on the left, one on the right, side by side. How am I able to make each one be of equal height, even though they have different content.</p>
<p> </p>
<p>For example, the right div has a lot of content, and is double the height of the left div, how do I make the left div stretch to the same height of the right div?</p>
<p> </p>
<p>Is there some JavaScript (jQuery) code to accomplish this?</p>
</div><!-- RIGHT COLUMN -->
</div><!-- MAIN CONTAINER -->
</div><!-- WRAPPER -->
</body>
</html>
This is what it looks like:
you can get it working with js:
<script>
$(document).ready(function() {
var height = Math.max($("#left").height(), $("#right").height());
$("#left").height(height);
$("#right").height(height);
});
</script>
I've seen many attempts to do this, though none met my OCD needs. You might need to dedicate a second to get your head around this, though it is better than using JavaScript.
Known downsides:
Does not support multiple element rows in case of a container with dynamic width.
Does not work in IE6.
The base:
red is (auxiliary) container that you would use to set margin to the content.
green is position: relative; overflow: hidden and (optionally, if you want columns to be centered) text-align: center; font-size: 0; line-height: 0;
blue display: block; float: left; or (optionally, if you want columns to be centered) display: inline-block; vertical-align: top;
So far nothing out of ordinary. Whatever content that blue element has, you need to add an absolutely positioned element (yellow; note that the z-index of this element must be lower than the actual content of the blue box) with this element and set top: 0; bottom: 0; (don't set left or right position).
All your elements now have equal height. For most of the layouts, this is already sufficient. My scenario required to have dynamic content followed by a static content, where static content must be on the same line.
To achieve this, you need to add padding-bottom (dark green) eq to the fixed height content to the blue elements.
Then within the yellow elements create another absolutely positioned (left: 0; bottom: 0;) element (dark blue).
Supposedly, if these boxes (yellow) had to be active hyperlinks and you had any style that you wanted to apply to the original blue boxes, you'd use adjacent sibling selector:
yellow:hover + blue {}
Here is a the code and demo:
HTML:
<div id="products">
<ul>
<li class="product a">
<a href="">
<p class="name">Ordinary product description.</p>
<div class="icon-product"></div>
</a>
<p class="name">Ordinary product description.</p>
</li>
<li class="product b">
<a href="">
<p class="name">That lenghty product description or whatever else that does not allow you have fixed height for these elements.</p>
<div class="icon-product"></div>
</a>
<p class="name">That lenghty product description or whatever else that does not allow you have fixed height for these elements.</p>
</li>
<li class="product c">
<a href="">
<p class="name">Another ordinary product description.</p>
<div class="icon-product"></div>
</a>
<p class="name">Another ordinary product description.</p>
</li>
</ul>
</div>
SCSS/LESS:
#products {
ul { position: relative; overflow: hidden; text-align: center; font-size: 0; line-height: 0; padding: 0; margin: 0;
li { display: inline-block; vertical-align: top; width: 130px; padding: 0 0 130px 0; margin: 0; }
}
li {
a { display: block; position: absolute; width: 130px; background: rgba(255,0,0,.5); z-index: 3; top: 0; bottom: 0;
.icon-product { background: #ccc; width: 90px; height: 90px; position: absolute; left: 20px; bottom: 20px; }
.name { opacity: 1; }
}
.name { position: relative; margin: 20px 10px 0; font-size: 14px; line-height: 18px; opacity: 0; }
a:hover {
background: #ddd; text-decoration: none;
.icon-product { background: #333; }
}
}
}
Note, that the demo is using a workaround that involves data-duplication to fix z-index. Alternatively, you could use pointer-events: none and whatever solution for IE.
here is very simple solution with a short css display:table
<div id="main" class="_dt-no-rows">
<div id="aside" contenteditable="true">
Aside
<br>
Here's the aside content
</div>
<div id="content" contenteditable="true">
Content
<br>
geht's pellentesque wurscht elementum semper tellus s'guelt Pfourtz !. gal hopla
<br>
TIP : Just clic on this block to add/remove some text
</div>
</div>
here is css
#main {
display: table;
width: 100%;
}
#aside, #content {
display: table-cell;
padding: 5px;
}
#aside {
background: none repeat scroll 0 0 #333333;
width: 250px;
}
#content {
background: none repeat scroll 0 0 #E69B00;
}
its look like this
Well, I don't do a ton of jQuery, but in the CSS/Javascript world I would just use the object model and write a statement as follows:
if(leftDiv.style.height > rightDive.style.height)
rightDiv.style.height = leftDiv.style.height;
else
leftDiv.style.height = rightDiv.style.height)
There's also a jQuery plugin called equalHeights that I've used with some success.
I'm not sure if the one I'm using is the one from the filament group mentioned above, or if it's this one that was the first google result... Either way a jquery plugin is probably the easiest, most flexible way to go.
Use this in jquery document ready function. Considering there are two divs having ids "left" and "right."
var heightR = $("#right").height();
var heightL = $("#left").height();
if(heightL > heightR){
$("#right").css({ height: heightL});
} else {
$("#left").css({ height: heightR});
}
Although many disagree with using javascript for this type of thing, here is a method that I used to acheive this using javascript alone:
var rightHeight = document.getElementById('right').clientHeight;
var leftHeight = document.getElementById('left').clientHeight;
if (leftHeight > rightHeight) {
document.getElementById('right').style.height=leftHeight+'px';
} else {
document.getElementById('left').style.height=rightHeight+'px';
}
With "left" and "right" being the id's of the two div tags.
This is what I use in plain javascript:
Seems long, but is very uncomplicated!
function equalizeHeights(elements){
//elements as array of elements (obtain like this: [document.getElementById("domElementId"),document.getElementById("anotherDomElementId")]
var heights = [];
for (var i=0;i<elements.length;i++){
heights.push(getElementHeight(elements[i],true));
}
var maxHeight = heights[biggestElementIndex(heights)];
for (var i=0;i<elements.length;i++){
setElementHeight(elements[i],maxHeight,true);
}
}
function getElementHeight(element, isTotalHeight){
// isTotalHeight triggers offsetHeight
//The offsetHeight property is similar to the clientHeight property, but it returns the height including the padding, scrollBar and the border.
//http://stackoverflow.com/questions/15615552/get-div-height-with-plain-javascript
{
isTotalHeight = typeof isTotalHeight !== 'undefined' ? isTotalHeight : true;
}
if (isTotalHeight){
return element.offsetHeight;
}else{
return element.clientHeight;
}
}
function setElementHeight(element,pixelHeight, setAsMinimumHeight){
//setAsMinimumHeight: is set, we define the minimum height, so it can still become higher if things change...
{
setAsMinimumHeight = typeof setAsMinimumHeight !== 'undefined' ? setAsMinimumHeight : false;
}
var heightStr = "" + pixelHeight + "px";
if (setAsMinimumHeight){
element.style.minHeight = heightStr; // pixels
}else{
element.style.height = heightStr; // pixels
}
}
function biggestElementIndex(arr){
//http://stackoverflow.com/questions/11301438/return-index-of-greatest-value-in-an-array
var max = arr[0];
var maxIndex = 0;
for (var i = 1; i < arr.length; i++) {
if (arr[i] > max) {
maxIndex = i;
max = arr[i];
}
}
return maxIndex;
}
I agree with initial answer but the JS solution with equal_heights() method does not work in some situations, imagine you have products next to each other. If you were to apply it only to the parent container yes they will be same height but the product name sections might differ if one does not fit to two line, this is where i would suggest using below
https://jsfiddle.net/0hdtLfy5/3/
function make_children_same_height(element_parent, child_elements) {
for (i = 0; i < child_elements.length; i++) {
var tallest = 0;
var an_element = child_elements[i];
$(element_parent).children(an_element).each(function() {
// using outer height since that includes the border and padding
if(tallest < $(this).outerHeight() ){
tallest = $(this).outerHeight();
}
});
tallest = tallest+1; // some weird shit going on with half a pixel or something in FF and IE9, no time to figure out now, sowwy, hence adding 1 px
$(element_parent).children(an_element).each(function() {
$(this).css('min-height',tallest+'px');
});
}
}

Categories

Resources