How to skip the matching the text in html attributes? - javascript

I want to get the index of matched string from the html to highlight the particular match in html.
The issue is I am using the tooltip with title in footnote link (which has footnote text). So.. when I am trying to highlight something in footnote it's highlighting in the footnote referenced tooltip.
Is there any way to skip the match of html attributes and jump to next match? (as I need the index of particular match so I can not use `$(selector).text();
please help me with this - below is the example of my code:
var selectedContent = $(selector).html();
var regex = new RegExp('/The text from footnote/', 'gi');
var indices = [];
while(result = regex.exec(selectedContent))
{
indices .push(result.index);
}
The regex is matching the text in footnote tooltip which is used as footnote reference.
My code sample is here. Please check it.

Try this:
var regex = new RegExp("(?!<[^>]+)" + textToHighlight + "(?![^<]+>)", 'gi');
var finalHtml = selectedContent.replace(regex, '<highlight class="highlight">'+textToHighlight+'</highlight>');
if(finalHtml)
$('#lipsumContainer').html(finalHtml);
[EDIT]
Here is a highlightText() function that searching for a string directly in textnodes, which means it won't affect attributes, however it won't find text if it's split between different elements.
In this example it will highlight selected text:
function highlightText(node, text) {
if (!node)
return;
if (node.nodeType == 3) { //process textnode
const index = node.data.indexOf(text);
if (index == -1)
return;
const textNodeMark = node.splitText(index),
textNodeAfter = textNodeMark.splitText(text.length),
mark = document.createElement("mark");
mark.appendChild(textNodeMark);
node.parentNode.insertBefore(mark, textNodeAfter);
} else {
for (let n of node.childNodes) {
if (n.tagName !== "MARK")
highlightText(n, text);
}
}
}
/* demo highlight selected text */
const container = document.getElementById("lipsumContainer");
container.addEventListener("click", e => {
const text = document.getSelection().toString().trim();
if (text === "")
return;
for(let m of container.querySelectorAll("mark"))
m.parentNode.replaceChild(m.firstChild, m); // remove previous marks;
container.normalize(); // join any split textnodes
highlightText(container, text); // add new highlighting
}, true);
$('#lipsum').on('mouseenter', '#_contentFoot1', function(){
var tooltipPosition = $(this).position();
var title = $(this).data('title');
var tooltipHtml = '<div class="tooltip">'+
'<span class="tooltiptext">'+title+'</span>'+'</div>';
$('body').append(tooltipHtml);
$('.tooltip').attr("style", "top: "+tooltipPosition.top+"px;left: "+tooltipPosition.left+"px;");
})
$('#lipsum').on('mouseleave', '#_contentFoot1', function(){
$('.tooltip').remove();
})
.tooltip .tooltiptext::after {
content: " ";
position: absolute;
top: 100%;
/* At the bottom of the tooltip */
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: black transparent transparent transparent;
}
.tooltip {
position: absolute;
display: inline-block;
border-bottom: 1px dotted black;
}
.tooltip .tooltiptext {
visibility: visible;
width: 120px;
background-color: black;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 0;
position: absolute;
z-index: 1;
bottom: 150%;
left: 50%;
margin-left: -60px;
}
mark {
background-color: lightgreen;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="lipsumContainer">
<div id="lipsum">
<p><b>bold</b> <i>italic</i></p>
<p><b>bold</b> <i>italic</i></p>
<p>
Vivamus vel commodo nisl, sed maximus lectus. Donec semper suscipit porta. Ut facilisis turpis pellentesque purus laoreet sagittis. Donec at scelerisque dui. Nullam hendrerit eros et lacinia venenatis. Nullam sodales nulla sit amet est ultrices,
sit amet tempus risus consequat.<a id="_contentFoot1" href="#_foot1" data-toglggle="tooltip" data-title="The 1st footnote text"><sup>[1]</sup></a> Fusce suscipit ipsum vel dapibus sagittis. Nullam tellus nisl, egestas ut feugiat non, porttitor
eget erat. Suspendisse placerat dictum nulla non sollicitudin. Maecenas nec tortor felis. Donec id cursus ligula, a volutpat sapien.
</p>
</div>
<div class="footnotes">
<p id="_foot1">
[1] <span>The 1st footnote text</span>
</p>
</div>
</div>

In the example, I have create a variable countMatch to skip the first match.
https://jsfiddle.net/f1sbh7x6/

Related

Smoothly Animating Image Position and Scale to Fit the Screen in HTML/CSS

I want to animate an image when clicked to fill the whole screen, in such a way that seamlessly transitions from its original position to its full size, and back again, like on Medium.
The problem here is that the CSS position property, with top and left, is not animatable. After trying that, I thought of using transform: scale() properties, but this will lead to a bunch of calculations that I'd like to avoid if possible.
My complicated solution would be to get the element's original position using getBoundingClientRect(), and from there find the end position the image must be in, and create a custom animation every time the image gets blown to full size using Element.animate. I'm not sure that's the best way to go about this, as figuring out the final size and position of the image will be some extra math I don't really want to mess with.
Below is my current markup, and some CSS showing it's possible to keyframe a positional animation using translateX() and translateY(), but not as I really need to.
document.querySelector('picture').onclick = function () {
document.querySelector('picture').classList.toggle('modal')
}
<style>
figure {
margin: 0 0 0 0;
display: inline-block; /* Stays same width as image contents */
background-color: whitesmoke;
}
img {
max-width: 100%; /* Images should fit within their container by default */
height: auto;
background-color: lightgrey;
margin: auto;
}
picture.modal {
position: fixed;
top: 0;
left: 0;
background-color: black;
height: 100vh;
width: 100vw;
margin: 0 0;
display: flex;
align-content: center;
object-fit: contain;
}
picture.modal img {
animation-name: slidein;
animation-duration: 1s;
}
#keyframes slidein {
0% {
transform: translateX(30px);
}
100% {
transform: translateX(0);
}
}
figcaption {
padding: 8px; /* Matches default page margin for Chrome/Edge */
}
</style>
<figure>
<picture>
<img src="https://c.pxhere.com/images/12/30/5e283733ff3cd2bd18d7cc13f40a-1435525.jpg!d" loading="auto" />
</picture>
<figcaption>
<header>Title</header>
<footer>Description</footer>
</figcaption>
</figure>
I started stubbing out some code as below, but quickly realized that another solution may be much better.
// Get the position of elements for animation
let x = document.querySelector('img').getBoundingClientRect().x
let y = document.querySelector('img').getBoundingClientRect().y
// Set the animation on the image so that it moves smoothly from its position outwards
Help with a vanilla CSS solution, if it is known, would be greatly appreciated.
You were going in the right direction with getBoundingClientRect. By using this and applying some calculations on it on, I was able to come up with this
let imageResizing = false;
function zoomUnzoomImage(resizeEvent) {
if (!resizeEvent && this.classList.contains('zoomed')) {
this.classList.remove('zoomed');
this.style.transform = "";
document.querySelector('.image-backdrop').classList.remove('zoomed');
removeZoomOutListeners();
removeResizeListener();
} else {
let imageCordinates
if (resizeEvent) {
imageCordinates = this._originalImageCordinates;
}
else {
imageCordinates = getBoundingClientRect(this);
this._originalImageCordinates = imageCordinates;
}
const deviceRatio = window.innerHeight / window.innerWidth;
const imageRatio = imageCordinates.height / imageCordinates.width;
// Scale image according to the device and image size
const imageScale = deviceRatio > imageRatio ?
window.innerWidth / imageCordinates.width :
window.innerHeight / imageCordinates.height;
const imageX = ((imageCordinates.left + (imageCordinates.width) / 2));
const imageY = ((imageCordinates.top + (imageCordinates.height) / 2));
const bodyX = (window.innerWidth) / 2;
const bodyY = (window.innerHeight) / 2;
const xOffset = (bodyX - imageX) / (imageScale);
const yOffset = (bodyY - imageY) / (imageScale);
this.style.transform = "scale(" + imageScale + ") translate(" + xOffset + "px," + yOffset + "px) ";
this.classList.add('zoomed');
document.querySelector('.image-backdrop').classList.add('zoomed');
registersZoomOutListeners();
registerResizeListener();
}
}
function registersZoomOutListeners() {
// zoom out on scroll
document.addEventListener('scroll', scrollZoomOut);
// zoom out on escape
document.addEventListener('keyup', escapeClickZoomOut);
// zoom out on clicking the backdrop
document.querySelector('.image-backdrop').addEventListener('click', backDropClickZoomOut);
}
function removeZoomOutListeners() {
document.removeEventListener('scroll', scrollZoomOut);
document.removeEventListener('keyup', escapeClickZoomOut);
document.querySelector('.image-backdrop').removeEventListener('click', backDropClickZoomOut);
}
function registerResizeListener() {
window.addEventListener('resize', onWindowResize)
}
function removeResizeListener() {
window.removeEventListener('resize', onWindowResize)
}
function scrollZoomOut() {
if (document.querySelector('.zoomable-image.zoomed') && !imageResizing) {
zoomUnzoomImage.call(document.querySelector('.zoomable-image.zoomed'));
}
}
function backDropClickZoomOut() {
if (document.querySelector('.zoomable-image.zoomed')) {
zoomUnzoomImage.call(document.querySelector('.zoomable-image.zoomed'));
}
}
function escapeClickZoomOut(event) {
if (event.key === "Escape" && document.querySelector('.zoomable-image.zoomed')) {
zoomUnzoomImage.call(document.querySelector('.zoomable-image.zoomed'));
}
}
function onWindowResize() {
imageResizing = true;
if (document.querySelector('.zoomable-image.zoomed')) {
debounce(
function () {
zoomUnzoomImage.call(document.querySelector('.zoomable-image.zoomed'), true)
imageResizing = false;
}, 100)()
}
}
function getBoundingClientRect(element) {
var rect = element.getBoundingClientRect();
return {
top: rect.top,
right: rect.right,
bottom: rect.bottom,
left: rect.left,
width: rect.width,
height: rect.height,
x: rect.x,
y: rect.y
};
}
function debounce(func, delay) {
let debounceTimer
return function () {
const context = this
const args = arguments
clearTimeout(debounceTimer)
debounceTimer
= setTimeout(() => func.apply(context, args), delay)
}
}
document.addEventListener('click', function (event) {
if (event && event.target && event.target.className.includes('zoomable-image')) {
zoomUnzoomImage.call(event.target)
}
});
figure {
margin: 0 0 0 0;
display: inline-block;
/* Stays same width as image contents */
background-color: whitesmoke;
}
img {
max-width: 100%;
/* Images should fit within their container by default */
height: auto;
background-color: lightgrey;
margin: auto;
transition: transform 0.3s;
}
.zoomable-image {
cursor: zoom-in;
}
.zoomable-image.zoomed {
cursor: zoom-out;
z-index: 100;
position: relative;
}
.image-backdrop.zoomed {
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
z-index: 50;
background-color: rgba(255, 255, 255, 0.95);
}
<div class="image-grid">
<img class="zoomable-image" src="https://picsum.photos/200/400?random=1" loading="auto" />
<img class="zoomable-image" src="https://picsum.photos/400/200?random=2" loading="auto" />
<img class="zoomable-image" src="https://picsum.photos/600/200?random=3" loading="auto" />
<img class="zoomable-image" src="https://picsum.photos/600/100?random=3" loading="auto" />
<img class="zoomable-image" src="https://picsum.photos/100/400?random=4" loading="auto" />
<img class="zoomable-image" src="https://picsum.photos/400/100?random=5" loading="auto" />
<img class="zoomable-image" src="https://picsum.photos/1000?random=6" loading="auto" />
<img class="zoomable-image" src="https://picsum.photos/300/400?random=7" loading="auto" />
<img class="zoomable-image" src="https://picsum.photos/400/300?random=8" loading="auto" />
</div>
<div class="image-backdrop"></div>
Here is another idea that I use. Similar to Medium's zoom effect.
const {
fromEvent
} = rxjs;
const images = document.querySelectorAll('article img');
const detailModal = document.querySelector('#detail-modal');
const detailBgModal = document.querySelector('.bg');
let canShowModal = true;
detailBgModal.addEventListener("transitionend", () => {
if (detailBgModal.style.opacity === '0') {
const showImage = document.querySelector('[fullscreen=true]')
showImage.style.zIndex = 0;
detailBgModal.style.bottom = 'auto';
showImage.removeAttribute('fullscreen')
canShowModal = true;
}
});
const checkIsImagePortrait = (src) => {
return new Promise((resolve) => {
const img = new Image();
img.src = src;
img.onload = () => {
let isImagePortrait;
const ratio = img.naturalWidth / img.naturalHeight;
const pratio = window.innerWidth / window.innerHeight;
console.log('pratio', pratio)
if (ratio < pratio) {
isImagePortrait = true;
} else {
isImagePortrait = false
}
resolve(isImagePortrait);
};
});
};
const showModal = (imageElement) => {
const src = imageElement.getAttribute('src');
const modalImage = document.querySelector('#detail-modal img');
return checkIsImagePortrait(src).then(isPortrait => {
const src = imageElement.getAttribute('src');
if (isPortrait) {
modalImage.style.height = '100%';
modalImage.style.width = 'auto';
} else {
modalImage.style.height = 'auto';
modalImage.style.width = '100%';
}
detailModal.style.top = `${window.scrollY}px`;
detailModal.style.height = `${window.innerHeight}px`;
detailModal.style.display = 'flex';
detailBgModal.style.bottom = '0';
detailBgModal.style.opacity = 1;
document.querySelector('#detail-modal img').setAttribute('src', src);
});
};
const hideModal = () => {
detailBgModal.style.opacity = 0;
detailModal.style.display = 'none';
canShowModal = false;
};
let modalDetailPos;
const handleBodyScroll = () => {
const {
scrollY
} = window;
if (Math.abs(scrollY - modalDetailPos) > 50) {
const event = new Event('click');
detailModal.dispatchEvent(event);
window.removeEventListener('scroll', handleBodyScroll);
}
};
images.forEach((image) => {
fromEvent(image, 'click').subscribe(() => {
if (!canShowModal) {
return
}
image.setAttribute('fullscreen', true)
console.log('show image')
showModal(image).then(() => {
const modalImage = document.querySelector('#detail-modal img');
const firstSnap = image.getBoundingClientRect();
const lastSnap = modalImage.getBoundingClientRect();
const {
deltaX,
deltaY,
deltaWidth,
deltaHeight
} = getDelta(firstSnap, lastSnap);
modalImage.animate([{
transformOrigin: 'top left',
transform: `
translate(${deltaX}px, ${deltaY}px)
scale(${deltaWidth}, ${deltaHeight})
`
},
{
transformOrigin: 'top left',
transform: 'none'
}
], {
duration: 300,
easing: 'ease-in-out',
fill: 'both'
}).onfinish = () => {
modalDetailPos = window.scrollY;
window.addEventListener('scroll', handleBodyScroll)
};
});
});
})
const moveElementToFullscreen = (element) => {
element.style.position = 'fixed';
element.style.left = 0;
element.style.top = 0;
element.style.right = 0;
element.style.bottom = 0;
};
const moveElementToNormalState = (element) => {
element.style.position = null;
element.style.left = null;
element.style.top = null;
element.style.right = null;
element.style.bottom = null;
};
const getDelta = (firstSnap, lastSnap) => {
const deltaX = firstSnap.left - lastSnap.left;
const deltaY = firstSnap.top - lastSnap.top;
const deltaWidth = firstSnap.width / lastSnap.width;
const deltaHeight = firstSnap.height / lastSnap.height;
return {
deltaX: deltaX,
deltaY: deltaY,
deltaWidth: deltaWidth,
deltaHeight: deltaHeight
};
}
fromEvent(detailModal, 'click').subscribe(() => {
const showImage = document.querySelector('[fullscreen=true]');
if (!showImage) {
return;
}
const modalImage = document.querySelector('#detail-modal img');
console.log('showImage', showImage)
const firstSnap = modalImage.getBoundingClientRect();
const lastSnap = showImage.getBoundingClientRect();
hideModal();
const {
deltaX,
deltaY,
deltaWidth,
deltaHeight
} = getDelta(firstSnap, lastSnap);
showImage.style.zIndex = 100;
showImage.animate([{
transformOrigin: 'top left',
transform: `
translate(${deltaX}px, ${deltaY}px)
scale(${deltaWidth}, ${deltaHeight})
`
},
{
transformOrigin: 'top left',
transform: 'none'
}
], {
duration: 400,
easing: 'ease',
fill: 'both'
});
});
article {
max-width: 700px;
margin: 0 auto;
padding: 20px;
box-sizing: border-box;
}
p {
font-family: 'Nunito';
font-size: 18px;
color: rgba(0, 0, 0, .84);
line-height: 1.60;
margin: 30px auto;
}
article img {
max-width: 100%;
display: block;
position: relative;
cursor: zoom-in;
}
#detail-modal {
justify-content: center;
align-items: center;
display: none;
position: absolute;
left: 0;
right: 0;
top: 0;
}
#detail-modal img {
display: block;
position: relative;
z-index: 100;
cursor: zoom-out;
}
.bg {
position: fixed;
left: 0;
top: 0;
right: 0;
background-color: rgba(0,0,0,.3);
opacity: 0;
display: block;
transition: opacity .3s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.4.0/rxjs.umd.min.js"></script>
<article>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut rutrum mauris id nibh ultrices, vitae hendrerit nibh venenatis. Phasellus volutpat mauris in diam lacinia, sit amet blandit ante scelerisque. Mauris porttitor risus sit amet urna vestibulum
porta. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer id diam sem. Nunc commodo, est sed efficitur condimentum, massa purus facilisis tellus, at commodo ex est a tellus. Morbi quis iaculis mi. Nam et
iaculis sapien, at mattis ipsum.</p>
<div>
<img src="https://images.unsplash.com/photo-1507358522600-9f71e620c44e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2850&q=80" />
</div>
<p>Nullam non porttitor nibh. Etiam mollis libero turpis, vitae sagittis ipsum gravida nec. Vivamus diam sapien, laoreet vel mi ultrices, efficitur tristique nunc. Nam tempus pharetra felis, nec condimentum leo vehicula a. Duis rutrum orci a tellus tristique
scelerisque. Suspendisse potenti. Proin mollis turpis feugiat, pulvinar risus ac, scelerisque diam. Aenean sodales venenatis tellus, in lacinia sapien. Nam tempus efficitur ligula id feugiat. Donec pretium, nunc sit amet dignissim rutrum, urna est
tristique ante, id convallis arcu urna vel dui. Cras a metus id orci aliquet tincidunt eget ac mi. Pellentesque elementum lorem in elementum vehicula. Nunc et dolor orci. Nulla varius lorem metus, vel cursus leo ultricies non.</p>
<div>
<img src="https://images.unsplash.com/photo-1548636200-691c76f69390?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=668&q=80" />
</div>
<p>
Aliquam at arcu mauris. Curabitur tincidunt massa ut sem porttitor ornare. Duis dapibus dignissim lectus. Cras sodales urna vitae libero lobortis, in consequat dolor efficitur. Sed eleifend nibh mi, sit amet euismod sem faucibus sed. Aenean ac accumsan
libero, ut dictum ex. Aenean tincidunt gravida enim, in luctus ante volutpat eu. Curabitur sed orci nec nisi cursus blandit.
</p>
<div>
<img src="https://images.unsplash.com/reserve/fPuLkQNXRUKI6HQ2cMPf_IMG_4761.jpg?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1650&q=80" />
</div>
<p>
Morbi ac quam luctus, aliquam odio in, consectetur orci. Etiam et dui sollicitudin, congue odio sit amet, commodo metus. Nunc ac facilisis dolor, sit amet dignissim dui. Praesent vehicula ut dui hendrerit commodo. Vivamus ac elementum turpis. Proin non
erat semper, dignissim risus vel, ornare libero. Ut volutpat libero non lacus eleifend ultrices. Morbi augue massa, placerat eget eros vel, consequat tincidunt sapien. Vestibulum placerat diam placerat tincidunt lacinia. Proin lorem justo, viverra
pretium laoreet eu, condimentum et odio. Proin vitae nibh felis.
</p>
</article>
<div class="bg"></div>
<div id="detail-modal">
<img />
</div>
quoted from here

CSS transition animation order issue for accordion

I have exampled below two div boxes with <h4> headers and followed with <p> with text. In an accordion style, I want to click each title and see the text appear, I've done this by setting the text div container box to max-height: 0; and opacity: 0; then max height a value and opacity 1 on click.
My issue is that I expect the animation to happen simultaneously for both boxes (one that slides up and hides, the other that slides down and appears), However, I get the animation running in order slide up and disappear forwarded by slide down and appear.
How can I get them both at the same time?
document.addEventListener('DOMContentLoaded', function() {
// main click handler for the whole page
document.addEventListener('click', function(event) {
var clickedElem = event.target;
if (clickedElem.matches(".slide-card h4")) {
var activeCards = document.querySelectorAll(".card-active");
if (activeCards) {
for (var i = 0; i < activeCards.length; i++) {
if (activeCards[i]) toggle(activeCards[i], "card-active");
}
}
toggle(clickedElem.parentNode, "card-active");
}
});
});
function toggle(el, elClass) {
if (el.matches("." + elClass)) {
el.classList.remove(elClass);
} else {
el.classList.add(elClass);
}
}
h4 {
cursor: pointer !important;
}
.slide-card {
border: 1px solid white;
padding: 1em;
}
.animated-slides-cards {
background: pink;
padding: 1em;
}
.card-body {
opacity: 0;
max-height: 0;
transition: all 0.75s ease-out;
}
.card-active .card-body {
opacity: 1;
max-height: 400px;
}
<div class="animated-slides-cards">
<div data="0" class="slide-card block-padding margin-bottom-05x card-active">
<h4 class="blue">Harness the power of the crowd</h4>
<div class="grey card-body">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis eros vitae metus sodales eget suscipit purus rhoncus. Proin ultrices gravida dolor, non porttitor enim interdum vitae. Integer feugiat lacinia tincidunt. Nulla laoreet tristique
tristique. Sed elementum justo a nisl elementum sit amet accumsan nisi tempor. Nulla quis eros et massa dignissim imperdiet a vitae purus.</p>
</div>
</div>
<div data="1" class="slide-card block-padding margin-bottom-05x">
<h4 class="blue">Slots straight into your systems</h4>
<div class="grey card-body">
<p>Using simple APIs, SmartCrowd™ connects to your systems and allocates tasks to ambassadors using your existing digital channels. You can opt for full integration or simply use Limitless Live Messenger on your website to maintain a single customer
view.</p>
</div>
</div>
</div>

MDL - Change + icon to - when clicked?

I have an accordion in place using MDL and JS, I would like to use the + icon when the accordion is closed and when it is clicked and open I would like to display a subtract icon (-).
Would I need to do this through the MDL divs or within the JS? I know I can put in some JS which will change the icon but not sure how to link it with the MDL icons...
Heres what I have so far.
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}
#padButton {
padding: 0px;
background-color: white;
width: 100%;
/* border: 2px; */
/* border-color: red; */
}
/* Style the buttons that are used to open and close the accordion panel */
.accordion {
background-color: rgb(255, 255, 255);
font-family: 'Courier New', Courier, monospace;
font-size: 24px;
color: #444;
cursor: pointer;
padding: 18px;
/* width: 100%; */
text-align: left;
vertical-align: center;
border: 2px;
outline: 2cm;
border-color: #777;
transition: 0.4s;
/* box-shadow: 0 0px 0px 1px rgba(0, 0, 0, 0.1); */
/* border-radius: 2px; */
/* overflow: hidden; */
}
/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */
.active,
.accordion:hover {
background-color: rgb(255, 255, 255);
}
/* Style the accordion panel. Note: hidden by default */
.panel {
padding: 0px 18px;
font-family: 'Courier New', Courier, monospace;
/* background-color: grey; */
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
/* border: 2px; */
box-shadow: 0 0px 0px 1px rgba(0, 0, 0, 0.05);
}
.panel:after {
padding: 10px 18px;
}
.accordion:after {
/* content: '\02795'; Unicode character for "plus" sign (+) */
font-size: 13px;
color: #777;
float: right;
margin-left: 5px;
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.grey-amber.min.css" />
</head>
<div id="padButton" class="mdl-cell mdl-cell--6-col">
<div class="accordion">Accordion Section 1
<div align="right">
<button class="mdl-button mdl-js-button mdl-button--fab mdl-button--mini-fab mdl-js-ripple-effect mdl-button--colored">
<i class="material-icons">add</i>
</button>
</div>
</div>
<div class="panel">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sed feugiat lectus. Phasellus maximus, ex nec bibendum volutpat, justo erat pellentesque massa, vel malesuada sem lectus tincidunt orci. Aenean eleifend est sit amet dapibus ullamcorper.
Nam ornare finibus ex vitae interdum. Sed quis purus eros. Sed ut porttitor dolor, eget ultrices justo. Sed eu leo porta, mattis libero eu, sagittis metus. Vivamus nec lobortis nisi. Suspendisse posuere enim arcu, at interdum dui congue vitae. Aliquam
vestibulum accumsan magna. Vivamus a arcu nunc. Cras euismod lacinia augue sed elementum. Phasellus dignissim semper mi at sollicitudin. Vivamus maximus semper nulla. Donec luctus, dolor non viverra aliquam, enim arcu imperdiet sem, et pretium dui
ante ac lectus.</p>
</div>
</div>
Here is the codepen of one of the ways to solve it:
https://codepen.io/mlzsolti/pen/jvKVpM
Basically, what you have to do, is when you toggle the accordion, also toggle the right icon of the tag. One of the methods in this case is to set the element's innerHTML to the desired icon's name.
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
var icon = this.querySelector("i");
if (this.classList.contains("active")) {
icon.innerHTML = "remove";
} else {
icon.innerHTML = "add";
}
if (panel.style.maxHeight){
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}
You just need to change the value of i tag. For example, to convert plus icon to minus you should change the value to remove
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var icon = this.querySelector("i");
var panel = this.nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
icon.innerHTML = "add";
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
icon.innerHTML = "remove";
}
});
}
#padButton {
padding: 0px;
background-color: white;
width: 100%;
/* border: 2px; */
/* border-color: red; */
}
/* Style the buttons that are used to open and close the accordion panel */
.accordion {
background-color: rgb(255, 255, 255);
font-family: 'Courier New', Courier, monospace;
font-size: 24px;
color: #444;
cursor: pointer;
padding: 18px;
/* width: 100%; */
text-align: left;
vertical-align: center;
border: 2px;
outline: 2cm;
border-color: #777;
transition: 0.4s;
/* box-shadow: 0 0px 0px 1px rgba(0, 0, 0, 0.1); */
/* border-radius: 2px; */
/* overflow: hidden; */
}
/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */
.active,
.accordion:hover {
background-color: rgb(255, 255, 255);
}
/* Style the accordion panel. Note: hidden by default */
.panel {
padding: 0px 18px;
font-family: 'Courier New', Courier, monospace;
/* background-color: grey; */
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
/* border: 2px; */
box-shadow: 0 0px 0px 1px rgba(0, 0, 0, 0.05);
}
.panel:after {
padding: 10px 18px;
}
.accordion:after {
/* content: '\02795'; Unicode character for "plus" sign (+) */
font-size: 13px;
color: #777;
float: right;
margin-left: 5px;
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.grey-amber.min.css" />
</head>
<div id="padButton" class="mdl-cell mdl-cell--6-col">
<div class="accordion">Accordion Section 1
<div align="right">
<button class="mdl-button mdl-js-button mdl-button--fab mdl-button--mini-fab mdl-js-ripple-effect mdl-button--colored">
<i class="material-icons">add</i>
</button>
</div>
</div>
<div class="panel">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sed feugiat lectus. Phasellus maximus, ex nec bibendum volutpat, justo erat pellentesque massa, vel malesuada sem lectus tincidunt orci. Aenean eleifend est sit amet dapibus ullamcorper.
Nam ornare finibus ex vitae interdum. Sed quis purus eros. Sed ut porttitor dolor, eget ultrices justo. Sed eu leo porta, mattis libero eu, sagittis metus. Vivamus nec lobortis nisi. Suspendisse posuere enim arcu, at interdum dui congue vitae. Aliquam
vestibulum accumsan magna. Vivamus a arcu nunc. Cras euismod lacinia augue sed elementum. Phasellus dignissim semper mi at sollicitudin. Vivamus maximus semper nulla. Donec luctus, dolor non viverra aliquam, enim arcu imperdiet sem, et pretium dui
ante ac lectus.</p>
</div>
</div>
This is an approach where the logic is completely in the CSS. This means you can change what icon is used by only changing the CSS, not having to touch the HTML or JavaScript.
In the HTML, change the button to this:
<button class="mdl-button mdl-js-button mdl-button--fab mdl-button--mini-fab mdl-js-ripple-effect mdl-button--colored">
<i class="material-icons"><span class=buttontoggle></span></i>
</button>
You basically just have an empty span. The CSS will then fill in the correct content.
In the CSS, add these rules to decide the content based on the presence of the active class:
.accordion.active .buttontoggle::after {
content: "remove"
}
.accordion:not(.active) .buttontoggle::after {
content: "add"
}
Insert "remove" after the buttontoggle span when it is inside an accordian with the active class.
Insert "add" after the buttontoggle span when it is inside an accordian without the active class.
Codepen: https://codepen.io/anon/pen/Kxeaxe
PURE CSS solution (No JS)
Found in here a bit extended: https://stackoverflow.com/a/49626209/1920145
Nevertheless I mannaged to do it even quicker...
Simply put NO CONTENT on your usual material-icons element, and adjust it with CSS for the CSS case you may want. In the link a checkbox is used.
Like this:
input+label>i::before{content:"add_box";}
input:checked+label>i::before{content:"indeterminate_check_box";}
input+label+p+i::before{content:"add_box";}
input:checked+label+p+i::before{content:"indeterminate_check_box";}
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
<input type="checkbox" id="myCheck">
<label for="myCheck">Inside the Label: <i class="material-icons"></i></label>
<p>Outside the Label:</p><i class="material-icons"></i>

Injecting text into a div

I'm trying to inject the ➕ emoji (acting as a button) next to each message on a website. Example:
This is what I'm currently doing:
contentscript.js:
document.getElementsByClassName("className").innerHTML = "➕";
This isn't showing anything. I also presume it won't show to the complete left; how can I do this?
If there is only one item in the DOM with the class name you want to add the item to you could do this
document.getElementsByClassName("className")[0].innerHTML += "➕";
Using the += operator will append the new value to the current value of the innerHTML
Otherwise you would have to loop over the collection of elements returned (see #Alex K comment above)
document.getElementsByClassName("className")[0].innerHTML += "<span class='icon'>➕</span>";
document.getElementsByClassName("icon")[0].addEventListener("click", clickFunction);
function clickFunction(){
alert("Clicked"+ this);
}
/* add whatever styles you need here */
.className {
position: relative;
padding-left: 20px;
}
.icon {
color: red;
position: absolute;
top: 0;
left: 0;
}
<div class="className">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam iaculis non quam non tristique. Cras ut imperdiet quam, pellentesque finibus nisl. Sed egestas dapibus turpis a rhoncus.</div>

gradient border with javascript

I need to set border color for a round div with javascript. User picks two colors (red and blue, for example) and the border becomes red on top and blue on bottom. But from the sides the border should change from red to blue, vice versa. In other words, I need to make vertical gradient for div's borders.
I can make it with just css, but since I don't know which colors would be chosen, I need javascript to do that for me.
A pure-javascript solution to do just that. Note that you need to do a funky getCssValuePrefix to determine which browser, thus calling the appropriate linear-gradient function.
var clicky = document.getElementById("changeButton"),
color1 = document.getElementById("color1"),
color2 = document.getElementById("color2"),
changeDiv = document.getElementsByClassName("foo")[0];
clicky.addEventListener("click", function(evt){
evt.preventDefault();
if (color1.value && color2.value) {
var myGradientString = getCssValuePrefix()+"linear-gradient("+color1.value+", "+color2.value+")";
changeDiv.style.background = myGradientString;
}
});
function getCssValuePrefix()
{
var rtrnVal = '';//default to standard syntax
var prefixes = ['-o-', '-ms-', '-moz-', '-webkit-'];
// Create a temporary DOM object for testing
var dom = document.createElement('div');
for (var i = 0; i < prefixes.length; i++)
{
// Attempt to set the style
dom.style.background = prefixes[i] + 'linear-gradient(#000000, #ffffff)';
// Detect if the style was successfully set
if (dom.style.background)
{
rtrnVal = prefixes[i];
}
}
dom = null;
delete dom;
return rtrnVal;
}
.foo {
border: 1px solid black;
background-image: linear-gradient(red, blue);
}
label {
display: block;
}
<div class="foo">
<p>Vivamus suscipit tortor eget felis porttitor volutpat. Sed porttitor lectus nibh. Donec rutrum congue leo eget malesuada. Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Curabitur aliquet quam id dui posuere blandit. Cras ultricies ligula sed magna dictum porta. Quisque velit nisi, pretium ut lacinia in, elementum id enim. Curabitur aliquet quam id dui posuere blandit. Praesent sapien massa, convallis a pellentesque nec, egestas non nisi. Sed porttitor lectus nibh.</p>
</div>
<label for="color1">Color 1: <input type="text" name="color1" id="color1" value="#ee3"></label>
<label for="color2">Color 2: <input type="text" name="color2" id="color2" value="#336"></label>
<button id="changeButton">Make it so!</button>
I think this is the best solution working exactly how you want :) I have tasted that on Chrome, MF and Opera. Paste my demo to empty html file. Good luck!
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<style>
#test
{
width: 300px;
height: 300px;
border-width: 3px;
border-style: solid;
border-image:
linear-gradient(blue, red) 1;
}
</style>
<body>
<input id="first" type="text">
<input id="second" type="text">
<input type="submit" onclick="changeColor()">
<div id="test"> </div>
<script>
var one = document.getElementById("first");
var two = document.getElementById("second");
var testDiv = document.getElementById("test");
function changeColor()
{
testDiv.style.borderImage = "linear-gradient(" + one.value + ", " + two.value + ") 1 ";
}
</script>
</body>
</html>

Categories

Resources