I've got a function on a drop-down button which when scrolled past it, it changes position to fixed so that it's always visible. Although, my problem is when it changes to position:fixed, it's usually aligned to the right, but it changes position to the left. How can I make it so that it stays in place? I can't use any fixed "right" value, as I need this to work on mobile version as well(width of parent container varies). Check my jsFiddle https://jsfiddle.net/ramisrour/2asco9n1/6/
Also, the .dropContainer doesn't need height or width, I just set it there for the fiddle, so you can test with the scrolling.
<div class="dropContainer">
<div class="dropDwn">
<div class="dropToggle">Viktig informasjon! Les her <i class="bouncer"></i></div>
<div class="dropContentBox">
<div class="dropTxt">
Vær oppmerksom på at Huawei P40-serien og Mate Xs ikke har Google Mobile Services (GMS) installert (Du kan derfor ikke laste ned apper direkte fra Google Play Butikken). Istedenfor har den AppGallery, Huaweis egen offisielle appbutikk.
</br>Du kan bruke AppGallery til å lete etter, laste ned, håndtere og dele mobilapper.
</div>
</div>
<div class="acceptCta"><span class="acceptCtaTxt">Jeg har lest og forstått </span><i class="arroww"></i></div>
</div>
</div>
.dropContainer{
position: relative;
box-sizing: border-box;
}
.dropDwn {
font-family: inherit;
background-color: #fff;
color: #333;
border: solid 1px #333;
position: relative;
text-align: center;
display: block;
z-index: 9999;
cursor: pointer;
padding: 5px;
border-radius: 5px;
box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.4);
transition: all 0.5s ease;
font-size: 16px;
width: 250px;
box-sizing: border-box;
height: 30px;
overflow: hidden;
float: right;
}
.dropDwn.open {
height: 280px;
width: 320px;
cursor: default;
background-color: #000E52;
color: #fff;
}
.dropTxt{
margin: 10px;
}
.bouncer {
position: relative;
border: solid #333;
border-width: 0 3px 3px 0;
display: inline-block;
padding: 3px;
transform: rotate(45deg);
-webkit-transform: rotate(45deg);
transition: all 0.5s ease;
animation: bouncer 2s infinite;
}
.dropDwn.open .bouncer {
transform: rotate(225deg);
border-color: #fff;
}
.dropContentBox {
margin-top: 10px;
display: inline-block;
color: #fff;
transition: all 0.5s ease;
text-align: center;
}
.acceptCta {
display: block;
position: relative;
cursor: pointer;
text-align: center;
margin: 0 auto;
background-color: #7CBD2B;
color: #333;
height: 35px;
width: 220px;
font-size: 14px;
font-weight: 600;
padding: 10px 25px;
box-sizing: border-box;
border-radius: 3px;
box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15);
transition: all 0.5s ease;
z-index: 10;
}
.acceptCta:hover {
background-color: #88D41B;
padding: 9px 24px;
}
.acceptCtaTxt {
display: inline-block;
float: left;
vertical-align: middle;
position: relative;
}
.arroww {
border: solid #333;
border-width: 0 3px 3px 0;
display: inline-block;
box-sizing: border-box;
padding: 3px;
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
transition: all 0.5s ease;
}
.acceptCta:hover .arroww {
/*padding: 6px 2px;
transform: rotate(-315deg);*/
}
#keyframes bouncer {
0% {
bottom: 0px;
}
20% {
bottom: 7px;
}
40% {
bottom: 0px;
}
60% {
bottom: 7px;
}
80% {
bottom: 0px;
}
100% {
bottom: 0px;
}
}
// Open/close
$(document).ready(function() {
$('.dropToggle').click(function() {
$(this).parent().addClass("open");
});
setTimeout(function() {
$('.acceptCta').click(function() {
//This needed
var $this = $(this);
var $container = $('.dropDwn');
var $arrow = $('.arroww');
$arrow.css("transform", "rotate(-315deg)");
$arrow.css("padding", "6px 2px");
setTimeout(function() {
$this.parent().removeClass("open");
}, 600);
setTimeout(function() {
$container.css("opacity", "0");
$container.css("right", "-1000px");
}, 1100);
setTimeout(function() {
$container.css("display", "none");
}, 1600);
});
})
});
// Hide if src image is in viewport, otherwise show
$(document).ready(function() {
var topOfOthDiv = $("[alt='Guide for installasjon av apper']").offset().top;
$(window).scroll(function() {
if ($(window).scrollTop() > topOfOthDiv + 200) {
$(".dropDwn").css("right", "-1000px");
$(".dropDwn").css("opacity", "0");
} else {
$(".dropDwn").css("opacity", "1");
}
});
});
// Stick button when scrolling past it
$(document).ready(function() {
var topOfOthDiv2 = $('.dropDwn').offset().top;
var drop = $('.dropDwn');
$(window).scroll(function() {
if ($(window).scrollTop() > topOfOthDiv2 + 20) {
drop.css("position", "fixed");
} else {
drop.css("position", "relative");
}
});
});
It's the bottom jQuery function which makes it stick by scrolling.
you have to add right value when you apply fixed position. in simalr way you can add top value too.
Update below js and also the yo
if ($(window).scrollTop() > topOfOthDiv2 + 20) {
drop.css("position", "fixed");
drop.css("right", "10px");
} else {
drop.css("position", "relative");
drop.css("right", "0px");
}
I solved this by using flex. In case anybody needs help with this here's what I did:
Max-width: 1280px; on the container, because it never gets bigger than 1280px. Added display: flex; and justify-content: flex-end; so the child element would always sit at the end of the parent element, even in fixed position.
Also added some margin and top values to make the transition from absolute to fixed more smoothly, but that might differ for you as this suited my situation.
.dropContainer{
display: flex;
max-width: 1280px;
justify-content: flex-end;
position: relative;
box-sizing: border-box;
}
.dropDwn {
font-family: inherit;
background-color: #fff;
color: #333;
border: solid 1px #333;
position: absolute;
text-align: center;
display: block;
z-index: 9999;
cursor: pointer;
padding: 5px;
border-radius: 5px;
box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.4);
transition: all 0.5s ease;
font-size: 16px;
width: 250px;
box-sizing: border-box;
height: 30px;
overflow: hidden;
float: right;
margin-right: 10px;
}
// Hide if src image is in viewport, show if not
$(document).ready(function() {
var topOfOthDiv2 = $('.dropDwn').offset().top;
var drop = $('.dropDwn');
var cont = $('.dropContainer');
$(window).scroll(function() {
if ($(window).scrollTop() > topOfOthDiv2 - 10) {
drop.css("position", "fixed");
drop.css("top", "10px");
} else {
drop.css("position", "absolute");
drop.css("top", "");
}
});
});
Related
I'm trying to do something like this ... User clicks the button called "debug" and then the window scrolls to a div (#div2) with a "thank you" text inside, then (after 2 seconds with #div2 on the screen) I want the window to scroll to ANOTHER div (#div3) with the rest of the content. The problem is that I want to do everything with a smooth scroll, I tried it by myself for 2 days but with no results yet :(
Here's the code:
HTML:
<div id="div1">
<h2>hi! my name is <b>Alan Aguilar</b></h2>
<h3>and i'm a web develop...</h3>
<p class="pIn">ummm...</p>
<p class="pAp">sorry can u press this button for me, please?</p>
debug
</div>
<div id="div2">
<h2>Th4nk5.</h2>
</div>
<div id="div3">
<h2>Thanks.</h2>
</div>
CSS:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
scroll-behavior: smooth;
}
#div1 h2 {
animation: opIn 2s backwards;
}
#div1 h3 {
animation: opIn 1.2s 1.4s backwards;
}
#div1 {
height: 650px;
display: flex;
justify-content: center;
flex-direction: column;
text-align: left;
margin-left: 300px;
}
#div1 h2, h3 {
font-weight: normal;
font-size: 30px;
margin-bottom: 10px;
}
#div1 h3 {
font-size: 23px;
}
.pIn {
font-size: 17px;
margin-top: 30px;
animation: opIn .5s 2.5s backwards;
}
.pAp {
font-size: 17px;
margin-top: 5px;
animation: opIn .5s 3.8s backwards;
}
#div2 {
display: flex;
justify-content: center;
align-items: center;
height: 700px;
background: linear-gradient(to top, #000000 0%, #ffffff 100%);
}
#div1 a {
width: 50px;
padding: 5px;
margin-top: 10px;
position: relative;
left: 245px;
text-decoration: none;
background-color: rgba(0, 0, 0, .1);
border: 1px solid rgba(0, 0, 0, .5);
border-radius: 2px;
animation: opIn .3s 4.5s backwards;
color: #000000;
}
#div1 a:hover {
background-color: rgba(0, 0, 0, .15);
box-shadow: inset 0px 0px 1px #000000;
}
#div2 h2 {
font-size: 190px;
}
#div3 {
display: flex;
justify-content: center;
align-items: center;
height: 700px;
background-color: #000000;
color: #ffffff;
}
#keyframes opIn {
from {opacity: 0;}
to {opacity: 1;}
}
Javascript:
window.onbeforeunload = function () {
window.scrollTo(0, 0);
}
// --------------------------------------------------------------------------
// THIS CODE IS NOT MINE, I FOUND IT ON THIS PAGE LOOKING FOR THE SAME TOPIC.
$(document).ready(function(){
// Add smooth scrolling to all links
$("a").on('click', function(event) {
// Make sure this.hash has a value before overriding default behavior
if (this.hash !== "") {
// Prevent default anchor click behavior
event.preventDefault();
// Store hash
var hash = this.hash;
// Using jQuery's animate() method to add smooth page scroll
// The optional number (800) specifies the number of milliseconds it takes to scroll to the specified area
$('html, body').animate({
scrollTop: $(hash).offset().top
}, 800, function(){
// Add hash (#) to URL when done scrolling (default click behavior)
window.location.hash = hash;
});
} // End if
});
});
// --------------------------------------------------------------------------
$("a").click(function() {
setTimeout(function() {window.location = "#div3"}, 1500, function() {
})
})
Select the element with the id div3, get the offset top, and animate using jQuery's animate function:
window.onbeforeunload = function() {
window.scrollTo(0, 0);
}
// --------------------------------------------------------------------------
// THIS CODE IS NOT MINE, I FOUND IT ON THIS PAGE LOOKING FOR THE SAME TOPIC.
$(document).ready(function() {
// Add smooth scrolling to all links
$("a").on('click', function(event) {
// Make sure this.hash has a value before overriding default behavior
if (this.hash !== "") {
// Prevent default anchor click behavior
event.preventDefault();
// Store hash
var hash = this.hash;
// Using jQuery's animate() method to add smooth page scroll
// The optional number (800) specifies the number of milliseconds it takes to scroll to the specified area
$('html, body').animate({
scrollTop: $(hash).offset().top
}, 800, function() {
// Add hash (#) to URL when done scrolling (default click behavior)
window.location.hash = hash;
});
} // End if
});
});
// --------------------------------------------------------------------------
$("a").click(function() {
setTimeout(function() {
$('html, body').animate({
scrollTop: $("#div3").offset().top
}, 800);
}, 1500)
})
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
scroll-behavior: smooth;
}
#div1 h2 {
animation: opIn 2s backwards;
}
#div1 h3 {
animation: opIn 1.2s 1.4s backwards;
}
#div1 {
height: 650px;
display: flex;
justify-content: center;
flex-direction: column;
text-align: left;
margin-left: 300px;
}
#div1 h2,
h3 {
font-weight: normal;
font-size: 30px;
margin-bottom: 10px;
}
#div1 h3 {
font-size: 23px;
}
.pIn {
font-size: 17px;
margin-top: 30px;
animation: opIn .5s 2.5s backwards;
}
.pAp {
font-size: 17px;
margin-top: 5px;
animation: opIn .5s 3.8s backwards;
}
#div2 {
display: flex;
justify-content: center;
align-items: center;
height: 700px;
background: linear-gradient(to top, #000000 0%, #ffffff 100%);
}
#div1 a {
width: 50px;
padding: 5px;
margin-top: 10px;
position: relative;
left: 245px;
text-decoration: none;
background-color: rgba(0, 0, 0, .1);
border: 1px solid rgba(0, 0, 0, .5);
border-radius: 2px;
animation: opIn .3s 4.5s backwards;
color: #000000;
}
#div1 a:hover {
background-color: rgba(0, 0, 0, .15);
box-shadow: inset 0px 0px 1px #000000;
}
#div2 h2 {
font-size: 190px;
}
#div3 {
display: flex;
justify-content: center;
align-items: center;
height: 700px;
background-color: #000000;
color: #ffffff;
}
#keyframes opIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="div1">
<h2>hi! my name is <b>Alan Aguilar</b></h2>
<h3>and i'm a web develop...</h3>
<p class="pIn">ummm...</p>
<p class="pAp">sorry can u press this button for me, please?</p>
debug
</div>
<div id="div2">
<h2>Th4nk5.</h2>
</div>
<div id="div3">
<h2>Thanks.</h2>
</div>
Making a sliding button to switch website theme using a CSS variable and javascript. It is working properly except there is a small bug that I am unable to fix. If I reload the page is light theme, the functionality of the button is being reversed. The "On" state of the button turns on light mode and off state toggles dark mode. However, the initial configuration is entirely opposite.
As you can see in the executable code snippet below, I tried solving this using click() function. This problem arises only when the value of num is 0 and page is reloaded. So, I thought if I store a variable in localStorage as false and check its value at the beginning of the function and if its false, then click the button and dont execute function, if its not false, then execute normally.
But it is not working for some reason. Please check this code:
if (!localStorage.getItem('thisvarisgud4me')) {
localStorage.setItem("thisvarisgud4me", "1")
}
document.getElementById("btn").addEventListener("click", change);
var c = "true";
if (!localStorage.getItem("clickc"))
{
localStorage.setItem("clickc", c);
}
function change() {
if (localStorage.getItem("clickc") == "false") {
localStorage.setItem("clickc","true");
document.getElementById("btn").click();
}
else if (localStorage.getItem("clickc") == "true") {
if (localStorage.getItem('thisvarisgud4me') == '1') {
localStorage.setItem("thisvarisgud4me", '0')
} else {
localStorage.setItem("thisvarisgud4me", '1')
}
var num = Number(localStorage.getItem('thisvarisgud4me'));
let root = document.documentElement;
root.style.setProperty("--numvar", num);
console.log(num);
if (num == 0) {
window.addEventListener("beforeunload", function (event) {
console.log("The page is redirecting")
alert("Reload");
localStorage.setItem("clickc", "false");
// document.getElementById("btn").click();
// debugger;
});
}
}
}
var num = Number(localStorage.getItem('thisvarisgud4me'));
let root = document.documentElement;
root.style.setProperty("--numvar", num);
:root {
--numvar: 0;
}
html {
filter: invert(var(--numvar));
}
body {
background: #fff;
}
.outer-button {
display: inline-block;
height: 28px;
width: 28px;
position: relative;
margin: 0 3px;
}
.inner-button {
display: inline-block;
position: absolute;
width: 100%;
height: 100%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), inset 0px 0px 1px 2px white;
border-radius: 20px;
background: #f5f5f5;
}
span {
display: inline-block;
vertical-align: middle;
}
.status-text {
color: white;
text-transform: uppercase;
font-size: 11px;
font-family: Futura, sans-serif;
transition: all 0.2s ease;
}
.sliding-switch {
height: 28px;
width: 72px;
position: relative;
}
.outer-switch-box {
overflow: hidden;
height: 100%;
width: 100%;
display: inline-block;
border-radius: 20px;
position: relative;
box-shadow: inset 0 1px 3px 0px #818181, 0px 1px 2px 1px white;
transition: all 0.3s ease;
transition-delay: 65ms;
position: absolute;
z-index: 1;
}
.inner-switch-box {
position: relative;
width: 175px;
transition: all 0.3s ease;
}
/* .switch-checkbox:checked+.outer-switch-box .unchecked-text {
color: transparent;
}
.switch-checkbox:not(:checked)+.outer-switch-box .checked-text {
color: transparent;
} */
.switch-checkbox:checked+.outer-switch-box .inner-switch-box {
left: -27px;
/*OFF*/
}
.switch-checkbox:not(:checked)+.outer-switch-box .inner-switch-box {
left: 20px;
/*ON*/
}
.switch-checkbox:checked+.outer-switch-box {
/* background-image: linear-gradient(#b6d284, #b6d284); */
background: #492d7b;
/* background: #b6d284; */
}
.switch-checkbox:not(:checked)+.outer-switch-box {
/* background-image: linear-gradient(#cbcbcb, #dbdbdb); */
background: #dbdbdb;
}
[type="checkbox"] {
margin: 0;
padding: 0;
appearance: none;
width: 100%;
height: 100%;
border: 1px solid black;
position: absolute;
top: 0;
left: 0;
z-index: 100;
opacity: 0;
}
.unchecked-text {
color: black !important;
font-weight: 700;
}
.btn-heading {
color: black;
font-family: 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Open Sans', 'Helvetica Neue', 'sans-serif';
padding: .4vw 0;
}
body {
float: left;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<body>
<div class="btn-heading">Dark Mode</div>
<div class="sliding-switch">
<input type="checkbox" id="btn" class="switch-checkbox" />
<div class="outer-switch-box">
<div class="inner-switch-box">
<span class="status-text checked-text" id="textp1">on</span>
<span class="outer-button">
<span class="inner-button"></span>
</span>
<span class="status-text unchecked-text" id="textp2">off</span>
</div>
</div>
</div>
</body>
</html>
As you might have noticed, I also tried manipulating CSS pseudo class properties using JS. But that was a complete mess. Then, I thought of this approach and I was quite confident that it is correct but looks like I was wrong :(
Just adding a condition to setting "clickc" to "true" will probably do the trick. Here I've used a similar condition to that you've already used for the "thisvarisgud4me" key.
I took the opportunity to test out a utility I created that essentially implements the Storage API (that's what <script src="https://heretic-monkey.link/FauxStorage.js"></script> is in the HTML, and why all of your localStorage references now say localStore).
So if you decide to copy and paste this into your own code, just do a search and replace of localStore with localStorage.
if (!localStore.getItem('thisvarisgud4me')) {
localStore.setItem("thisvarisgud4me", "1")
}
document.getElementById("btn").addEventListener("click", change);
var c = "true";
if (!localStore.getItem("clickc")) {
localStore.setItem("clickc", c);
}
function change() {
if (localStore.getItem("clickc") == "false") {
document.getElementById("btn").click();
localStore.getItem("clickc") = "true";
} else if (localStore.getItem("clickc") == "true") {
if (localStore.getItem('thisvarisgud4me') == '1') {
localStore.setItem("thisvarisgud4me", '0')
} else {
localStore.setItem("thisvarisgud4me", '1')
}
var num = Number(localStore.getItem('thisvarisgud4me'));
let root = document.documentElement;
root.style.setProperty("--numvar", num);
console.log(num);
if (num == 0) {
window.addEventListener("beforeunload", function(event) {
console.log("The page is redirecting")
alert("Reload");
localStore.setItem("clickc", "false");
// document.getElementById("btn").click();
// debugger;
});
}
}
}
var num = Number(localStore.getItem('thisvarisgud4me'));
let root = document.documentElement;
root.style.setProperty("--numvar", num);
:root {
--numvar: 0;
}
html {
filter: invert(var(--numvar));
}
body {
background: #fff;
}
.outer-button {
display: inline-block;
height: 28px;
width: 28px;
position: relative;
margin: 0 3px;
}
.inner-button {
display: inline-block;
position: absolute;
width: 100%;
height: 100%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), inset 0px 0px 1px 2px white;
border-radius: 20px;
background: #f5f5f5;
}
span {
display: inline-block;
vertical-align: middle;
}
.status-text {
color: white;
text-transform: uppercase;
font-size: 11px;
font-family: Futura, sans-serif;
transition: all 0.2s ease;
}
.sliding-switch {
height: 28px;
width: 72px;
position: relative;
}
.outer-switch-box {
overflow: hidden;
height: 100%;
width: 100%;
display: inline-block;
border-radius: 20px;
position: relative;
box-shadow: inset 0 1px 3px 0px #818181, 0px 1px 2px 1px white;
transition: all 0.3s ease;
transition-delay: 65ms;
position: absolute;
z-index: 1;
}
.inner-switch-box {
position: relative;
width: 175px;
transition: all 0.3s ease;
}
/* .switch-checkbox:checked+.outer-switch-box .unchecked-text {
color: transparent;
}
.switch-checkbox:not(:checked)+.outer-switch-box .checked-text {
color: transparent;
} */
.switch-checkbox:checked+.outer-switch-box .inner-switch-box {
left: -27px;
/*OFF*/
}
.switch-checkbox:not(:checked)+.outer-switch-box .inner-switch-box {
left: 20px;
/*ON*/
}
.switch-checkbox:checked+.outer-switch-box {
/* background-image: linear-gradient(#b6d284, #b6d284); */
background: #492d7b;
/* background: #b6d284; */
}
.switch-checkbox:not(:checked)+.outer-switch-box {
/* background-image: linear-gradient(#cbcbcb, #dbdbdb); */
background: #dbdbdb;
}
[type="checkbox"] {
margin: 0;
padding: 0;
appearance: none;
width: 100%;
height: 100%;
border: 1px solid black;
position: absolute;
top: 0;
left: 0;
z-index: 100;
opacity: 0;
}
.unchecked-text {
color: black !important;
font-weight: 700;
}
.btn-heading {
color: black;
font-family: 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Open Sans', 'Helvetica Neue', 'sans-serif';
padding: .4vw 0;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://heretic-monkey.link/FauxStorage.js"></script>
</head>
<body>
<div class="btn-heading">Dark Mode</div>
<div class="sliding-switch">
<input type="checkbox" id="btn" class="switch-checkbox" />
<div class="outer-switch-box">
<div class="inner-switch-box">
<span class="status-text checked-text" id="textp1">on</span>
<span class="outer-button">
<span class="inner-button"></span>
</span>
<span class="status-text unchecked-text" id="textp2">off</span>
</div>
</div>
</div>
</body>
</html>
Here's how I would refactor it. This is more of an object-oriented way of doing things; it might not appeal to everyone and it certainly isn't meant to. It works for me and I'm the only one I need to make happy with it :).
class ThemeStore {
_darkModeKey = "thisvarisgud4me";
_darkMode = null;
get darkMode() {
if (this._darkMode === null) {
if (!localStore.getItem(this._darkModeKey)) {
localStore.setItem(this._darkModeKey, 0);
}
this._darkMode = JSON.parse(localStore.getItem(this._darkModeKey));
}
return this._darkMode;
}
set darkMode(value) {
this._darkMode = value;
}
persist() {
localStore.setItem("thisvarisgud4me", JSON.stringify(this.darkMode));
}
}
var themeStore = new ThemeStore();
document.getElementById("btn").addEventListener("click", change);
function change(e) {
themeStore.darkMode = e.target.checked ? 0 : 1;
let root = document.documentElement;
root.style.setProperty("--numvar", themeStore.darkMode);
console.log(themeStore.darkMode);
if (themeStore.darkMode === 0) {
window.addEventListener("beforeunload", function(event) {
console.log("The page is redirecting")
themeStore.persist();
});
}
}
document.getElementById("btn").dispatchEvent(new CustomEvent("change"));
:root {
--numvar: 0;
}
html {
filter: invert(var(--numvar));
}
body {
background: #fff;
}
.outer-button {
display: inline-block;
height: 28px;
width: 28px;
position: relative;
margin: 0 3px;
}
.inner-button {
display: inline-block;
position: absolute;
width: 100%;
height: 100%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), inset 0px 0px 1px 2px white;
border-radius: 20px;
background: #f5f5f5;
}
span {
display: inline-block;
vertical-align: middle;
}
.status-text {
color: white;
text-transform: uppercase;
font-size: 11px;
font-family: Futura, sans-serif;
transition: all 0.2s ease;
}
.sliding-switch {
height: 28px;
width: 72px;
position: relative;
}
.outer-switch-box {
overflow: hidden;
height: 100%;
width: 100%;
display: inline-block;
border-radius: 20px;
position: relative;
box-shadow: inset 0 1px 3px 0px #818181, 0px 1px 2px 1px white;
transition: all 0.3s ease;
transition-delay: 65ms;
position: absolute;
z-index: 1;
}
.inner-switch-box {
position: relative;
width: 175px;
transition: all 0.3s ease;
}
/* .switch-checkbox:checked+.outer-switch-box .unchecked-text {
color: transparent;
}
.switch-checkbox:not(:checked)+.outer-switch-box .checked-text {
color: transparent;
} */
.switch-checkbox:checked+.outer-switch-box .inner-switch-box {
left: -27px;
/*OFF*/
}
.switch-checkbox:not(:checked)+.outer-switch-box .inner-switch-box {
left: 20px;
/*ON*/
}
.switch-checkbox:checked+.outer-switch-box {
/* background-image: linear-gradient(#b6d284, #b6d284); */
background: #492d7b;
/* background: #b6d284; */
}
.switch-checkbox:not(:checked)+.outer-switch-box {
/* background-image: linear-gradient(#cbcbcb, #dbdbdb); */
background: #dbdbdb;
}
[type="checkbox"] {
margin: 0;
padding: 0;
appearance: none;
width: 100%;
height: 100%;
border: 1px solid black;
position: absolute;
top: 0;
left: 0;
z-index: 100;
opacity: 0;
}
.unchecked-text {
color: black !important;
font-weight: 700;
}
.btn-heading {
color: black;
font-family: 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Open Sans', 'Helvetica Neue', 'sans-serif';
padding: .4vw 0;
}
<script src="https://heretic-monkey.link/FauxStorage.js"></script>
<div class="btn-heading">Dark Mode</div>
<div class="sliding-switch">
<input type="checkbox" id="btn" class="switch-checkbox" />
<div class="outer-switch-box">
<div class="inner-switch-box">
<span class="status-text checked-text" id="textp1">on</span>
<span class="outer-button">
<span class="inner-button"></span>
</span>
<span class="status-text unchecked-text" id="textp2">off</span>
</div>
</div>
</div>
I have a JavaScript application where users can search for places on a map. The input text provides autocomplete suggestions: as the user types, some suggestions appear at the bottom of the text input itself.
I'm using a third party JavaScript autocomplete library which charge per user request.
Unfortunately each keystroke counts as a single request since the library doesn't implement any debouncing when receiving the onInput callback from the input element. So the suggestions look snappy but at a cost of many user requests.
What I'd like to do is to redefine the on input callback inside the input element to implement debouncing (let's say 500ms).
Since the third party library accepts the JavaScript element itself, I cannot use an external debouncing mechanism: (probably the library detects the onInput message sent by the input element itself)
var placesAutocomplete = places({
appId: 'xxxxxxxxxxx',
apiKey: 'kkkkkkkkkk',
container: document.querySelector('#inputelement'), // <- the library accepts the element itself, not an "on input" listener (which could be easily debounced by an external function)
language: G_lang
});
placesAutocomplete.on('change', function(res){
// user leaves the input text, set lat lon on my map (code not shown here on SO)
var lat = res.suggestion.latlng.lat;
var lon = res.suggestion.latlng.lng;
finish(lat, lon);
});
the debounce must be provided by the JavaScript element itself. So basically, the element should fire an onInput callback filtered by the debouncing mechanism.
Is it possible to do so by using vanilla JavaScript only?
EDIT
Looks like someone tried to make a pull request for a debounce feature on the GitHub project page but got rejected:
https://github.com/algolia/places/issues/281
and someone else forked the library and merged the pull request on its own fork -> https://github.com/AcuityScheduling/places/tree/feature/debounce
Using the official codepen, I made this hackish debounced version:
var client = algoliasearch("latency", "6be0576ff61c053d5f9a3225e2a90f76")
var index = client.initIndex('movies');
var myAutocomplete = autocomplete('#search-input', {
hint: false
}, [{
source: autocomplete.sources.hits(index, {
hitsPerPage: 5
}),
displayKey: 'title',
templates: {
suggestion: function(suggestion) {
var sugTemplate = "<img src='" + suggestion.image + "'/><span>" + suggestion._highlightResult.title.value + "</span>"
return sugTemplate;
}
}
}]).on('autocomplete:selected', function(event, suggestion, dataset) {
console.log(suggestion, dataset);
});
document.querySelector(".searchbox [type='reset']").addEventListener("click", function() {
document.querySelector(".aa-input").focus();
this.classList.add("hide");
myAutocomplete.autocomplete.setVal("");
});
document.querySelector("#debouncer").addEventListener("input", function() {
var s = document.querySelector("#search-input");
s.value = this.value;
clearTimeout(this.tick);
this.tick = setTimeout(() => s.dispatchEvent(new KeyboardEvent('input')), 500);
});
document.querySelector("#search-input").addEventListener("input", function() {
var searchbox = document.querySelector(".aa-input");
var reset = document.querySelector(".searchbox [type='reset']");
if (searchbox.value.length === 0) {
reset.classList.add("hide");
} else {
reset.classList.remove('hide');
}
});
body {
padding: 60px;
}
.searchbox {
display: inline-block;
position: relative;
width: 200px;
height: 37px;
white-space: nowrap;
box-sizing: border-box;
font-size: 13px;
font-family: arial, sans-serif;
}
#debouncer {
position: absolute;
z-index: 1;
box-sizing:border-box;
padding: 0 30px 0 37px;
width: 100%;
height: 100%;
vertical-align: middle;
white-space: normal;
font-size: inherit;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background: none;
border:0;
}
.algolia-autocomplete {
display: block;
height: 100%;
}
.searchbox__wrapper {
width: 100%;
height: 100%;
}
.searchbox__input {
display: inline-block;
-webkit-transition: box-shadow .4s ease, background .4s ease;
transition: box-shadow .4s ease, background .4s ease;
border: 0;
border-radius: 19px;
box-shadow: inset 0 0 0 1px #D9D9D9;
color:transparent;
background: #FFFFFF;
padding: 0;
padding-right: 30px;
padding-left: 37px;
width: 100%;
height: 100%;
vertical-align: middle;
white-space: normal;
font-size: inherit;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.searchbox__input::-webkit-search-decoration,
.searchbox__input::-webkit-search-cancel-button,
.searchbox__input::-webkit-search-results-button,
.searchbox__input::-webkit-search-results-decoration {
display: none;
}
#debouncer:hover ~ * .searchbox__input {
box-shadow: inset 0 0 0 1px silver;
}
#debouncer:focus ~ * .searchbox__input,
#debouncer:active ~ * .searchbox__input {
outline: 0;
box-shadow: inset 0 0 0 1px #4098CE;
background: #FFFFFF;
}
#debouncer::-webkit-input-placeholder {
color: #AAAAAA;
}
#debouncer::-moz-placeholder {
color: #AAAAAA;
}
#debouncer:-ms-input-placeholder {
color: #AAAAAA;
}
#debouncer::placeholder {
color: #AAAAAA;
}
.searchbox__submit {
position: absolute; z-index:2;
top: 0;
right: inherit;
left: 0;
margin: 0;
border: 0;
border-radius: 18px 0 0 18px;
background-color: rgba(255, 255, 255, 0);
padding: 0;
width: 37px;
height: 100%;
vertical-align: middle;
text-align: center;
font-size: inherit;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.searchbox__submit::before {
display: inline-block;
margin-right: -4px;
height: 100%;
vertical-align: middle;
content: '';
}
.searchbox__submit:hover,
.searchbox__submit:active {
cursor: pointer;
}
.searchbox__submit:focus {
outline: 0;
}
.searchbox__submit svg {
width: 17px;
height: 17px;
vertical-align: middle;
fill: #666666;
}
.searchbox__reset {
position: absolute; z-index:2;
top: 8px;
right: 8px;
margin: 0;
border: 0;
background: none;
cursor: pointer;
padding: 0;
font-size: inherit;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
fill: rgba(0, 0, 0, 0.5);
}
.searchbox__reset.hide {
display: none;
}
.searchbox__reset:focus {
outline: 0;
}
.searchbox__reset svg {
display: block;
margin: 4px;
width: 13px;
height: 13px;
}
.searchbox__input:valid~.searchbox__reset {
display: block;
-webkit-animation-name: sbx-reset-in;
animation-name: sbx-reset-in;
-webkit-animation-duration: .15s;
animation-duration: .15s;
}
#-webkit-keyframes sbx-reset-in {
0% {
-webkit-transform: translate3d(-20%, 0, 0);
transform: translate3d(-20%, 0, 0);
opacity: 0;
}
100% {
-webkit-transform: none;
transform: none;
opacity: 1;
}
}
#keyframes sbx-reset-in {
0% {
-webkit-transform: translate3d(-20%, 0, 0);
transform: translate3d(-20%, 0, 0);
opacity: 0;
}
100% {
-webkit-transform: none;
transform: none;
opacity: 1;
}
}
.aa-dropdown-menu {
position: relative;
top: -6px;
border-radius: 3px;
margin: 6px 0 0;
padding: 0;
text-align: left;
height: auto;
position: relative;
background: transparent;
border: none;
width: 100%;
left: 0 !important;
box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.2), 0 2px 3px 0 rgba(0, 0, 0, 0.1);
}
.aa-dropdown-menu:before {
position: absolute;
content: '';
width: 14px;
height: 14px;
background: #fff;
z-index: 0;
top: -7px;
border-top: 1px solid #D9D9D9;
border-right: 1px solid #D9D9D9;
transform: rotate(-45deg);
border-radius: 2px;
z-index: 999;
display: block;
left: 24px;
}
.aa-dropdown-menu .aa-suggestions {
position: relative;
z-index: 1000;
}
.aa-dropdown-menu [class^="aa-dataset-"] {
position: relative;
border: solid 1px #D9D9D9;
border-radius: 3px;
overflow: auto;
padding: 8px 8px 8px;
}
.aa-dropdown-menu * {
box-sizing: border-box;
}
.aa-suggestion {
font-size: 1.1em;
padding: 4px 4px 0;
display: block;
width: 100%;
height: 38px;
clear: both;
}
.aa-suggestion span {
white-space: nowrap!important;
text-overflow: ellipsis;
overflow: hidden;
display: block;
float: left;
line-height: 2em;
width: calc(100% - 30px);
}
.aa-suggestion.aa-cursor {
background: #eee;
}
.aa-suggestion em {
color: #4098CE;
}
.aa-suggestion img {
float: left;
vertical-align: middle;
height: 30px;
width: 20px;
margin-right: 6px;
}
<script src="//cdn.jsdelivr.net/algoliasearch/3/algoliasearch.min.js"></script>
<script src="//cdn.jsdelivr.net/autocomplete.js/0/autocomplete.min.js"></script>
<form novalidate="novalidate" onsubmit="return false;" class="searchbox">
<div role="search" class="searchbox__wrapper">
<input id="debouncer" type="text" name="search" autocomplete="off" placeholder="Search for a movie">
<input id="search-input" type="search" name="autocomplete" autocomplete="off" required="required" class="searchbox__input">
<button type="submit" title="Submit your search query." class="searchbox__submit">
<svg role="img" aria-label="Search">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sbx-icon-search-13"></use>
</svg>
</button>
<button type="reset" title="Clear the search query." class="searchbox__reset hide">
<svg role="img" aria-label="Reset">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sbx-icon-clear-3"></use>
</svg>
</button>
</div>
</form>
<div class="svg-icons" style="height: 0; width: 0; position: absolute; visibility: hidden">
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="sbx-icon-clear-3" viewBox="0 0 40 40"><path d="M16.228 20L1.886 5.657 0 3.772 3.772 0l1.885 1.886L20 16.228 34.343 1.886 36.228 0 40 3.772l-1.886 1.885L23.772 20l14.342 14.343L40 36.228 36.228 40l-1.885-1.886L20 23.772 5.657 38.114 3.772 40 0 36.228l1.886-1.885L16.228 20z" fill-rule="evenodd"/></symbol>
<symbol id="sbx-icon-search-13" viewBox="0 0 40 40"><path d="M26.806 29.012a16.312 16.312 0 0 1-10.427 3.746C7.332 32.758 0 25.425 0 16.378 0 7.334 7.333 0 16.38 0c9.045 0 16.378 7.333 16.378 16.38 0 3.96-1.406 7.593-3.746 10.426L39.547 37.34c.607.608.61 1.59-.004 2.203a1.56 1.56 0 0 1-2.202.004L26.807 29.012zm-10.427.627c7.322 0 13.26-5.938 13.26-13.26 0-7.324-5.938-13.26-13.26-13.26-7.324 0-13.26 5.936-13.26 13.26 0 7.322 5.936 13.26 13.26 13.26z" fill-rule="evenodd"/></symbol>
</svg>
</div>
This approach is hardly better than this autocomplete implementation itself..
Hope it may help.
I think I managed to find a very good solution. I forked the library and enabled the debounce feature (which was already there, only disabled for the autocomplete form).
End result: 80% usage drop given a small autocomplete lag of 200ms, perfectly acceptable
I am creating a sort-of popup menu that is specific to each .smallCatalogBlock div. The circle you see under the title is the trigger. The issue I am having is that if you click on the blue circle, both popup menus fadeIn, when it should only be that specific one.
The same applies to the popup title. It uses only the first .smallCatalogBlock information, opposed to the one clicked on.
Does anyone know how I can leave this in the dynamic setup I am going for, while populating the specific information for the one clicked on?
var catalogName = $('.smallCatalogBlock').data('fill-specs');
//Filling Circle
$('.catalogSmallCircle').html(
'<div class="catalogSmallCircleIn" data-catalog-name=' + catalogName + '><div class="total-center"><div class="circlePlus"></div></div></div><div class="catalogCircleExpand"><div class="catalogExpandClose">x</div><div class="total-center expandText"><span class="catalogName pdfSubHeader"></span><p class="dGw circleExpandText"></p><button class="catalogDownload downloadButton" name="Profile_Catalog" data-catalog-now="Profile Small Catalog Button" data-catalog-view-name="Profile Catalog">View</button><button class="catalogDownload requestButton" data-catalog-name="Profile Catalog">Request</button></div></div>'
)
//Circle Expand
$('.catalogSmallCircleIn').on('click', function() {
// old $('.catalogSmallCircle').addClass('rectangle').find('.catalogSmallCircleIn').hide();
$(this).closest('.catalogSmallCircle').addClass('rectangle').find('.catalogSmallCircleIn').hide();
// old $('.catalogCircleExpand').fadeIn(100).addClass('rectangle');
//$(this).closest('.catalogCircleExpand').fadeIn(100).addClass('rectangle');
$('.catalogCircleExpand').fadeIn(100).addClass('rectangle');
//Getting Catalog Name
let catalogChoice = $(this).data('catalog-name');
$('.catalogName').html(catalogChoice);
event.stopPropagation();
});
//Close Circle
$('.catalogExpandClose').on('click', function(event) {
$('.catalogSmallCircle').removeClass('rectangle').find('.catalogSmallCircleIn').fadeIn();
$('.catalogCircleExpand').hide().removeClass('rectangle');
});
.smallCatalogWrap {
width: 100%;
height: auto;
margin: 60px 0;
}
.smallCatalogBlock {
width: 25%;
height: auto;
display: inline-block;
vertical-align: top;
margin: 20px auto;
text-decoration: none;
}
.smallCatalogTitle {
font-family: 'Nunito', sans-serif;
color: #4d4d4d;
font-size: 1.3rem;
text-align: center;
display: block;
font-weight: 400;
}
.smallCatalogButtonWrap {
margin-top: 15px;
width: 100%;
position: relative;
}
.catalogSmallCircle {
background: #225DB8;
width: 70px;
height: 70px;
position: absolute;
margin: 10px auto;
left: 90%;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
border-radius: 100%;
box-shadow: 0 0 20px rgba(0, 0, 0, .9);
border: 2px solid #FFF;
webkit-transition: all 1s;
transition: all 1s;
cursor: pointer;
}
.catalogSmallCircle.rectangle {
border-radius: 0;
border: 2px solid #094765;
background: linear-gradient(to bottom right, #225DB8, #4174C2);
width: 400px;
min-height: 200px;
webkit-transition: all 1s;
transition: all 1s;
transform: translate(-45%, -45%);
-webkit-transform: translate(-45%, -45%);
z-index: 1;
cursor: auto;
}
.catalogSmallCircleIn {
width: 100%;
height: 100%;
position: relative;
}
.circlePlus {
background-size: 30px 30px;
width: 30px;
height: 30px;
display: block;
margin: 0 auto;
z-index: 1;
}
.catalogCircleExpand {
height: 0;
display: none;
opacity: 0;
webkit-transition: all .5s;
transition: all .5s;
}
.catalogCircleExpand.rectangle {
opacity: 1;
height: auto;
webkit-transition: all .5s;
transition: all .5s;
transition-delay: .4s;
-webkit-transition-delay: .4s;
padding: 10px 0;
}
.expandText .catalogDownload {
font-size: 1.1rem;
padding: .7em 1.1em;
}
.expandText .pdfSubHeader {
font-size: 1.1rem;
}
.catalogExpandClose {
color: #FFF;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="smallCatalogWrap">
<div class="smallCatalogBlock" data-fill-specs="Catalog">
<span class="smallCatalogTitle">Catalog</span>
<div class="smallCatalogButtonWrap">
<div class="catalogSmallCircle"></div>
</div>
</div>
<div class="smallCatalogBlock" data-fill-specs="Technology">
<span class="smallCatalogTitle">Technology</span>
<div class="smallCatalogButtonWrap">
<div class="catalogSmallCircle"></div>
</div>
</div>
</div>
You have to loop over the smallCatalogBlocks and build them individually, otherwise they will all have the same catalog name. And then in your event handlers, you have to make all your selectors be contextual lookups.
I ran the modified code, and it appears to be building the circles correctly, however for some reason the text is not showing up on them, even though the text is there if you inspect the element. Didn't figure that part out, but this should show you at least how to do the contextual logic and the looping to build the elements.
$('.smallCatalogBlock').each(function(index, catalogBlock){
var catalogName = $(catalogBlock).data('fill-specs');
console.log(catalogName);
//Filling Circle
$('.catalogSmallCircle', catalogBlock).html(
'<div class="catalogSmallCircleIn" data-catalog-name='+ catalogName +'><div class="total-center"><div class="circlePlus"></div></div></div><div class="catalogCircleExpand"><div class="catalogExpandClose">x</div><div class="total-center expandText"><span class="catalogName pdfSubHeader"></span><p class="dGw circleExpandText"></p><button class="catalogDownload downloadButton" name="Profile_Catalog" data-catalog-now="Profile Small Catalog Button" data-catalog-view-name="Profile Catalog">View</button><button class="catalogDownload requestButton" data-catalog-name="Profile Catalog">Request</button></div></div>'
)
});
//Circle Expand
$('.catalogSmallCircleIn').on('click', function(event) {
var $smallCircle = $(this).closest('.catalogSmallCircle');
$smallCircle
.addClass('rectangle')
.find('.catalogSmallCircleIn')
.hide();
$smallCircle
.find('.catalogCircleExpand')
.fadeIn(100)
.addClass('rectangle');
//Getting Catalog Name
let catalogChoice = $(this).data('catalog-name');
console.log(catalogChoice);
$smallCircle.find('.catalogName').html(catalogChoice);
event.stopPropagation();
});
//Close Circle
$('.catalogExpandClose').on('click', function(event) {
var $smallCircle = $(this).closest('.catalogSmallCircle');
$smallCircle
.removeClass('rectangle')
.find('.catalogSmallCircleIn')
.fadeIn();
$smallCircle
.find('.catalogCircleExpand')
.hide()
.removeClass('rectangle');
});
.smallCatalogWrap {
width: 100%;
height: auto;
margin: 60px 0;
}
.smallCatalogBlock {
width: 25%;
height: auto;
display: inline-block;
vertical-align: top;
margin: 20px auto;
text-decoration: none;
}
.smallCatalogTitle {
font-family: 'Nunito', sans-serif;
color: #4d4d4d;
font-size: 1.3rem;
text-align: center;
display: block;
font-weight: 400;
}
.smallCatalogButtonWrap {
margin-top: 15px;
width: 100%;
position: relative;
}
.catalogSmallCircle {
background: #225DB8;
width: 70px;
height: 70px;
position: absolute;
margin: 10px auto;
left: 90%;
-webkit-transform: translateX(-50%);transform: translateX(-50%);
border-radius: 100%;
box-shadow: 0 0 20px rgba(0,0,0,.9);
border: 2px solid #FFF;
webkit-transition: all 1s;transition: all 1s;
cursor: pointer;
}
.catalogSmallCircle.rectangle {
border-radius: 0;
border: 2px solid #094765;
background: linear-gradient(to bottom right,#225DB8,#4174C2);
width: 400px;
min-height: 200px;
webkit-transition: all 1s; transition: all 1s;transform: translate(-45%, -45%);-webkit-transform: translate(-45%, -45%);
z-index: 1;
cursor: auto;
}
.catalogSmallCircleIn {
width: 100%;
height: 100%;
position: relative;
}
.circlePlus {
background-size: 30px 30px;
width: 30px;
height: 30px;
display: block;
margin: 0 auto;
z-index: 1;
}
.catalogCircleExpand {
height: 0;
display: none;
opacity: 0;
webkit-transition: all .5s;
transition: all .5s;
}
.catalogCircleExpand.rectangle {
opacity: 1;
height: auto;
webkit-transition: all .5s;
transition: all .5s;
transition-delay: .4s;
-webkit-transition-delay: .4s;
padding: 10px 0;
}
.expandText .catalogDownload {
font-size: 1.1rem;
padding: .7em 1.1em;
}
.expandText .pdfSubHeader {
font-size: 1.1rem;
}
.catalogExpandClose {
color: #FFF;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="smallCatalogWrap">
<div class="smallCatalogBlock" data-fill-specs="Catalog">
<span class="smallCatalogTitle">Catalog</span>
<div class="smallCatalogButtonWrap">
<div class="catalogSmallCircle"></div>
</div>
</div><div class="smallCatalogBlock" data-fill-specs="Technology">
<span class="smallCatalogTitle">Technology</span>
<div class="smallCatalogButtonWrap">
<div class="catalogSmallCircle"></div>
</div>
</div>
</div>
Long time lurker and first time poster here.
I have a javascript code that selects and copies rich text to the clipboard from a specific div.
It works well but there is an extra line break that I want to eliminate.
The relevant part of my code:
var textToSelect = document.getElementById('answerText');
range = document.createRange();
range.selectNode(textToSelect[0]);
window.getSelection().addRange(range);
document.execCommand("copy");
alert(range);
In div answerText the text I have is:
answer text
There aren't any spaces or line breaks before/after the text. The code results in the following message.
This extra line break is then also copied to the clipboard.
How can I remove the extra line break from the selection? I also prefer to check that the content I'm removing from my range is indeed a line break to make it safe to use.
Try to switch over to selectNodeContents() instead of selectNode() - so range.selectNodeContents(textToSelect[0]);.
I ran into the same problem trying to whip up some script to copy hex colors when the color block was clicked.
Here is the snippet to play with (remove contents and you'll see the extra line - at least in chrome):
$(function() {
//copys inner html of target div.
//event button
var copyBtn = $('.copy-btn');
copyBtn.on('click', function(event) {
var $this = $(this);
//find the element that has the text you want.
var content = $this.prev('.meta--value');
//creates new range object and sets text as boundaries.
var range = document.createRange();
range.selectNodeContents(content[0]);
window.getSelection().addRange(range);
try {
// Now that we've selected the text, execute the copy command
var successful = document.execCommand('copy');
/*var msg = successful ? 'successful' : 'unsuccessful';
console.log('Copy email command was ' + msg);*/
//handle success
$(this).after('<span class="success"></span>');
setTimeout(function() {
$('.success').addClass('show');
setTimeout(function() {
$('.success').fadeOut(function() {
$('.success').remove();
});
}, 1000);
}, 0);
} catch (err) {
//console.log('Oops, unable to copy');
}
//clear out range for next event.
window.getSelection().removeAllRanges();
});
});
#import url(https://fonts.googleapis.com/css?family=Roboto:300,900|Merriweather);
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
background-color: #efefef;
color: #131313;
}
p {
font-weight: 600;
font-size: 20px;
margin-top: 1.5vw;
}
.swatch-wrapper {
width: 90vw;
margin: 0 auto;
}
#media (max-width: 320px) {
.swatch-wrapper {
width: 100%;
margin: 0;
}
}
.swatch-wrapper .swatch {
display: inline-block;
vertical-align: top;
margin: 0 3px 10px 0;
width: 22.5vw;
max-width: 200px;
background-color: #e2e2e2;
box-shadow: 0 1px 0 0 rgba(19, 19, 19, 0.1);
-webkit-transition: box-shadow 150ms, -webkit-transform 150ms;
transition: box-shadow 150ms, -webkit-transform 150ms;
transition: transform 150ms, box-shadow 150ms;
transition: transform 150ms, box-shadow 150ms, -webkit-transform 150ms;
}
#media (max-width: 320px) {
.swatch-wrapper .swatch {
display: block;
width: 90%;
margin: 0 auto 1.5vh;
max-width: none;
}
}
.swatch-wrapper .swatch:hover {
-webkit-transform: scale(1.02);
transform: scale(1.02);
box-shadow: 0px 3px 10px -5px rgba(19, 19, 19, 0.3);
}
.swatch-wrapper .swatch--color {
margin: 0;
padding: 0;
width: 100%;
height: 5vw;
max-height: 133px;
}
.swatch-wrapper .swatch--meta {
padding: 0.375vw;
font-family: monospace;
}
#media (max-width: 320px) {
.swatch-wrapper .swatch--meta {
padding: .75vh .75vh 1vh;
}
}
.swatch-wrapper .meta + .meta {
margin-top: 0.375vw;
}
.swatch-wrapper .meta:before {
color: #939393;
}
.swatch-wrapper .meta--name:before {
content: "name: ";
}
.swatch-wrapper .meta--aliases:before {
content: 'aliases: ';
}
.swatch-wrapper .meta--value:before {
content: 'value: ';
}
/**
functional classes / none display stuff
*/
.cf:before, .cf:after {
content: ' ';
display: table;
}
.cf:after {
clear: both;
}
.swatch {
position: relative;
-webkit-transition: all .1s ease;
transition: all .1s ease;
}
.swatch:hover {
background-color: #EFEFEF;
}
.swatch:hover:after {
bottom: 0;
}
.swatch:hover .copy-btn {
opacity: .5;
right: 0;
}
.copy-btn {
position: absolute;
opacity: 0;
cursor: pointer;
top: 0;
text-align: center;
-webkit-transition: all .2s ease;
transition: all .2s ease;
right: -100%;
bottom: 0;
padding: 15px;
background-color: #636363;
color: #fff;
text-transform: uppercase;
font-size: 10px;
font-weight: 600;
border: none;
outline: none;
width: 100%;
}
.copy-btn:before {
content: "";
width: 10px;
height: 100%;
display: inline-block;
vertical-align: middle;
background-repeat: no-repeat;
background-position-y: center;
}
.copy-btn:after {
content: "click to copy";
display: inline-block;
vertical-align: middle;
}
.success {
position: absolute;
right: 0;
cursor: pointer;
width: 100%;
top: 0;
font-size: 12px;
text-align: center;
font-style: normal;
font-weight: 600;
bottom: 0;
padding: 15px;
background-color: #000;
color: #fff;
text-transform: uppercase;
-webkit-transition: all .1s ease;
transition: all .1s ease;
font-weight: 600;
-webkit-transform: scale(0.1);
transform: scale(0.1);
border-radius: 100%;
}
.success:before {
content: "";
width: 10px;
height: 100%;
display: inline-block;
vertical-align: middle;
background-repeat: no-repeat;
background-position-y: center;
}
.success:after {
content: "Copied!";
display: inline-block;
vertical-align: middle;
}
.success.show {
-webkit-transform: scale(1);
transform: scale(1);
border-radius: 0;
}
.p-success:hover {
border: 2px solid #E93937;
}
.p-success:before {
content: "Copied";
background: #E93937;
width: 92px;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<br>
<article class="swatch-wrapper cf">
<footnote>Hover and Click to Copy Hex</footnote>
<br>
<section class="swatch">
<figure class="swatch--color" style="background-color:#fff;"></figure>
<figcaption class="swatch--meta">
<div class="meta meta--name">Millennium</div>
<div class="meta meta--value">#ffffff</div>
<button class="copy-btn"></button>
</figcaption>
</section>
</article>
When you use selectNode(), the startContainer, endContainer, and commonAncestorContainer are all equal to the parent node of the node that was passed in; startOffset is equal to the index of the given node within the parent's childNodes collection, whereas endOffset is equal to the startOffset plus one (because only one node is selected).
When you use selectNodeContents(), startContainer, endContainer, and commonAncestorContainer are equal to the node that was passed in; startOffset is equal to 0; endOffset is equal to the number of child nodes (node.childNodes.length).
From: http://www.wrox.com/WileyCDA/Section/JavaScript-DOM-Ranges.id-292301.html