i ran into a problem when using an ordering function.
var $wrapper = $('#list');
$wrapper.find('.blogboxes').sort(function (a, b) {
return +b.dataset.date - +a.dataset.date;
})
.appendTo( $wrapper );
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="list">
<div class="blogboxes" data-date="2023. 01. 28."></div>
<div class="blogboxes" data-date="2023. 01. 29."></div>
<div class="blogboxes" data-date="2023. 01. 30."></div>
<div class="blogboxes" data-date="2023. 01. 24."></div>
</div>
if i put numbers in the data-date it works fine(desc), but with date it doesn't.
I get my date from a Sanity query in this format:
"2023-01-28T12:10:00.000Z"
which isn't even showing, so i use this:
{new Date(post.publishedAt).toLocaleDateString()}
Can i convert my query output to be orderable by the sort function?
Example in Jsfiddle
http://jsfiddle.net/5e4y9xbj/
Edit:
import React, {useState, useEffect} from "react";
import "./pagestyles.css"
import sanityClient from "../client.js"
import { NavLink } from "react-router-dom";
import $ from "jquery"
const orderBy = () => {
var $wrapper = $('#list');
$wrapper.find('.blogboxes').sort(function (a, b) {
return +b.dataset.date.replaceAll(/\. ?/g, '') - +a.dataset.date.replaceAll(/\. ?/g, '');
})
.appendTo( $wrapper );
};
export default function Blog() {
const [postData, setPost] = useState(null);
useEffect(()=>
{sanityClient.fetch('*[_type =="post"] {title, publishedAt, slug, extract, mainImage{asset->{_id, url},alt}}'
).then((data)=> setPost(data))
.catch(console.error);
},[]);
window.addEventListener('scroll', orderBy);
return (
<main>
<div className="kontener">
<h1>Blog</h1>
<div id="list" className="flex-container">
{ postData && postData.map((post, index)=>(
<div className="blogboxes" key={index} data-date={new Date(post.publishedAt).toLocaleDateString()} >
<div >
<div className="contentzoom" >
<img className="miniblog" src ={post.mainImage.asset.url}
alt ={post.mainImage.alt} />
</div>
<NavLink className="posztlink" to={"/blog/" + post.slug.current} key ={post.slug.current}>
{post.title}
</NavLink>
<div className="extract">{post.extract}</div>
</div>
</div> ))}
</div>
</div>
</main>
)
}
You can use the Javascript Date function for comparison of two dates inside the sort function for descending order.
For that you need to first convert your date to local date format with Javascript function and after that you can sort based on its value.
So now your final code will be :
var $wrapper = $('#list');
$wrapper.find('.blogboxes').sort(function (a, b) {
var dateA = new Date(a.dataset.date);
var dateB = new Date(b.dataset.date);
return dateB.getTime() - dateA.getTime();
})
.appendTo( $wrapper );
Result :
You can compare the strings. Remove the conversion to numbers:
var $wrapper = $('#list');
$wrapper.find('.blogboxes').sort(function (a, b) {
return b.dataset.date.localeCompare(a.dataset.date);
})
.appendTo( $wrapper );
div[data-date] {
font-family: sans-serif;
background: teal;
padding: 10px;
font-weight: bold;
color: #023636;
text-shadow: 0 1px 0 #58BBBB;
font-size: 20px;
width: 100px;
margin: 4px auto;
text-align: center;
}
div[data-date]:before {
content: attr(data-date);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="list">
<div class="blogboxes" data-date="2023. 01. 28."></div>
<div class="blogboxes" data-date="2023. 01. 29."></div>
<div class="blogboxes" data-date="2023. 01. 30."></div>
<div class="blogboxes" data-date="2023. 01. 24."></div>
</div>
Another approach is to convert the strings to a format that can be converted to numbers:
var $wrapper = $('#list');
$wrapper.find('.blogboxes').sort(function (a, b) {
return +b.dataset.date.replaceAll(/\. ?/g, '') - +a.dataset.date.replaceAll(/\. ?/g, '');
})
.appendTo( $wrapper );
div[data-date] {
font-family: sans-serif;
background: teal;
padding: 10px;
font-weight: bold;
color: #023636;
text-shadow: 0 1px 0 #58BBBB;
font-size: 20px;
width: 100px;
margin: 4px auto;
text-align: center;
}
div[data-date]:before {
content: attr(data-date);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="list">
<div class="blogboxes" data-date="2023. 01. 28."></div>
<div class="blogboxes" data-date="2023. 01. 29."></div>
<div class="blogboxes" data-date="2023. 01. 30."></div>
<div class="blogboxes" data-date="2023. 01. 24."></div>
</div>
Related
I have an html code from an online product recommendation platform which allows me to add JS to html.
The HTML code below works fine, but i want to apply JS to it based on the logic that, if ${discountvalue} shows any value, then both ${discountvalue} and ${regularvalue} values are visible with cross line on regularvalue.
If ${regularvalue} does not have any value or is empty/null, then only ${regularvalue} will be visible.
HTML CODE:
<div class="slider-wrapper">
${#Recommendations} // This loads the the more then one values of product recommendation
${discountvalue}
${regularvalue}
<div class="slider-wrapper">
<div class="pricebox">
<p class="dynamic-price">${discountvalue}</p>
<p class="regular-price">
<span class="rec_price_num">${regularvalue}</span>
</p>
</div>
</div>
${/#Recommendations}
</div>
And this is the JS code:
var itemPrices = document.querySelectorAll('.slider-wrapper .pricebox');
for(var i=0; i<itemPrices.length; i++){
var discount_price = itemPrices[i].getElementsByClassName("dynamic-price")[0].innerHTML;
var price = getElementsByClassName("rec_price_num")[0].innerHTML;
if(discount_price){
var discount_price = itemPrices[i].getElementsByClassName("dynamic-price")[0].innerHTML;
var price = getElementsByClassName("rec_price_num")[0].innerHTML;
}
else {
var price = getElementsByClassName("rec_price_num")[0].innerHTML;
}
}
parseFloat(discount_price.innerHTML) will return a truthy value if the discount price can be parsed to a floating point number different from 0.
You can use the CSS rule text-decoration: line-through; to have a cross line on the value.
var itemPrices = document.querySelectorAll('.slider-wrapper .pricebox');
for(var i=0; i<itemPrices.length; i++){
var discount_price = itemPrices[i].getElementsByClassName("dynamic-price")[0];
var price = itemPrices[i].getElementsByClassName("regular-price")[0];
if(parseFloat(discount_price.innerHTML)){
price.style.textDecoration = "line-through";
} else {
discount_price.style.display = "none";
}
}
<div class="slider-wrapper">
<div class="pricebox">
<p class="dynamic-price">0</p>
<p class="regular-price">5</p>
</div>
<hr>
<div class="pricebox">
<p class="dynamic-price">4</p>
<p class="regular-price">5</p>
</div>
</div>
Don't need javascript. can be solved by css3
.pricebox p {
font-weight: bold;
font-size: 1.5rem;
color: red;
margin: 0 0 1rem;
}
.pricebox .dynamic-price {
margin-bottom: 0;
}
.pricebox .dynamic-price:empty {
display: none
}
.pricebox .dynamic-price:not(:empty) + .regular-price {
font-size: .875rem;
color: gray;
text-decoration: line-through;
}
<div class="slider-wrapper">
<div class="slider-wrapper">
<div class="pricebox">
<p class="dynamic-price"></p>
<p class="regular-price">
<span class="rec_price_num">200.00</span>
</p>
</div>
<div class="pricebox">
<p class="dynamic-price">100.00</p>
<p class="regular-price">
<span class="rec_price_num">150.00</span>
</p>
</div>
</div>
</div>
I want to make that when the user clicks onto the bordered container, the 'Name' text should show the container's name only and the 'Subject' text should show the container's subject only, but this code shows all the elements inside the container for the 'Name' and the 'Subject' too.
I mean there are two elements inside one container. One with class 'name' and one with the class 'subject'. When I click onto the bordered container I want to get the 'name' text's and write it into the element with the class resname. And the same thing with the subject. Any idea how to solve it?
var name = document.querySelectorAll('.name');
var gname = $('.resname');
var gsub = $('.ressubject');
$('.container').click(function() {
gname.text($(this).text());
gsub.text($(this).text());
});
.container {
border: 1px solid red;
cursor: pointer;
padding: 5px;
}
.resname, .ressubject {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="header">
<span class="name">firstname</span>
</div>
<div class="body">
<span class="subject">firstsubject</span>
</div>
</div>
<br>
<div class="container">
<div class="header">
<span class="name">secondname</span>
</div>
<div class="body">
<span class="subject">secondsubject</span>
</div>
</div>
<hr><br>
<div class="result">
<span>Name: <span class="resname"></span></span><br>
<span>Subject: <span class="ressubject"></span></span>
</div>
is that what you want?
const container = document.querySelector('.container');
const output = document.querySelector('.output');
const outputItemName = output.querySelector('.output-item > span[data-name]');
const outputItemSubject = output.querySelector('.output-item > span[data-subject]');
container.addEventListener('click', (e) => {
const containerItem = e.target.closest('.container-item');
if (!containerItem) return;
const { name, subject } = containerItem.dataset;
outputItemName.innerText = name;
outputItemSubject.innerText = subject;
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container-inner>* {
margin-bottom: 16px;
}
.container-inner>*:last-of-type {
margin-bottom: 0;
}
.container-item {
padding: 8px;
border: 1px solid black;
cursor: pointer;
}
.output {
margin-top: 16px;
}
<div class="container">
<div class="container-inner">
<div class="container-item" data-name="First name" data-subject="First subject">
<div class="container-item-name">First name</div>
<div class="container-item-subject">First subject</div>
</div>
<div class="container-item" data-name="Second name" data-subject="Second subject">
<div class="container-item-name">Second name</div>
<div class="container-item-subject">Second subject</div>
</div>
</div>
</div>
<div class="output">
<div class="output-inner">
<div class="output-item">
<span>Name:</span>
<span data-name></span>
</div>
<div class="output-item">
<span>Subject:</span>
<span data-subject></span>
</div>
</div>
</div>
I need to list out a long name list inside my page while showing all names at first is not desirable.
So I try to add an expand more button on it.
However, using a button will keep the browser focus on that button after it's pressed, left the button position unchanged on the screen while the name was inserted before that button.
On the other hand, using any, not focusable element (eg. div with onclick function) will do the desired behavior but lost the accessibility at all. Making the "button" only clickable but not focusable.
How do I make the button flushed to list bottom like the snippet div block does? Or is there a better choice to expand the existing list?
const myArray = [
'Alex',
'Bob',
'Charlie',
'Dennis',
'Evan',
'Floron',
'Gorgious',
'Harris',
'Ivan',
'Jennis',
'Kurber',
'Lowrance',
]
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const handleExpand = e => {
setIdx(idx + 1)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div>
</div>
}
ReactDOM.render(<ExpandList/>, document.getElementById('root'))
.demo>p {
display: block;
padding: 20px;
color: #666;
background: #3331;
}
.demo>div>div {
display: flex;
padding: 15px;
margin-left: auto;
color: #666;
background: #3331;
}
.pointer {
cursor: pointer;
}
.pointer:hover {
background-color: #6663;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id='root' class='demo'>hello</div>
Removing focus from the button in the click handler is probably the most elegant approach: e.target.blur(). It will work on any HTML element, whether it is focusable or not (as with the div in your case).
const myArray = [
'Alex',
'Bob',
'Charlie',
'Dennis',
'Evan',
'Floron',
'Gorgious',
'Harris',
'Ivan',
'Jennis',
'Kurber',
'Lowrance',
]
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const handleExpand = e => {
e.target.blur()
setIdx(idx + 1)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div>
</div>
}
ReactDOM.render(<ExpandList/>, document.getElementById('root'))
.demo>p {
display: block;
padding: 20px;
color: #666;
background: #3331;
}
.demo>div>div {
display: flex;
padding: 15px;
margin-left: auto;
color: #666;
background: #3331;
}
.pointer {
cursor: pointer;
}
.pointer:hover {
background-color: #6663;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id='root' class='demo'>hello</div>
Inspired by #MiKo, temporally unmount the button after click and set a timeout to add it back seems to do the work. Since browser lose the focus on original expand button, this will keep content flush down without focusing the original button:
const ExpandList = (props) => {
const [idx, setIdx] = React.useState(8)
const [showBtn, setShowBtn] = React.useState(true)
const handleExpand = e => {
setShowBtn(false)
setIdx(idx + 1)
setTimeout(() => setShowBtn(true), 10)
}
return <div className='demo'>
<h1>Name List</h1>
{myArray.slice(0,idx).map(
name => <p key={name}>{name}</p>
)}
{showBtn?
<div>
<button onClick={handleExpand} children='Button Expand' className='pointer' />
<div onClick={handleExpand} className='pointer'>Div Expand</div>
</div> :
<div></div>
}
</div>
}
But I'm still looking a method that doesn't need to 'unmount' a thing which should be there all time.
I have a scenario like this. Say: let a = [2,5,6] and let b = [1,2,3,4,5,6,7,8]. Array b is displayed in boxes and revealed when one clicks any of the boxes. What I am trying to do is, when one clicks on any box and the value is the same as any value in array a, I replace the value with other unique values and if they're not the same I display as it is.
e.g. If I click a box that has a value of 2 or 5 or 6 i replace the values with the other values.
A minimal example is:
new Vue({
el: "#app",
data: {
a: [2,5,6],
b: [1,2,3,4,5,6,7,8]
},
methods: {
replaceNumber() {
// function to replace the values
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
.numbers {
display: flex;
}
li {
list-style-type: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Numbers:</h2>
<br/>
<ul class="numbers">
<li v-for="num in a">
{{num}}
</li>
</ul>
<br/>
<template>
<button #click="replaceNumber" v-for="number in b">
{{ number }}
</button>
</template>
</div>
Use indexOf() to locate the position of the element you want to replace.
Then use splice() together with the index you got to remove that element.
Then use splice() again to insert a new value to the same index.
Check the documentation of each method above to understand their syntax.
You can try with random numbers if found in first array i.e a
var a = [2,5,6]
var b = [1,2,3,4,5,6,7,8]
a.forEach(function(e){
$("#aDiv").append(`<h2>${e}</h2>`);
})
b.forEach(function(e){
$("#bDiv").append(`<h2 class="seconddiv">${e}</h2>`);
});
$(".seconddiv").on('click',function(){
let val= $(this).html();
if(a.includes(parseInt(val))){
var uniqueNo = 0;
do {
uniqueNo=getRandomInt(0,10);
}
while (a.includes(parseInt(uniqueNo)));
$(this).html(uniqueNo);
}
})
let getRandomInt= (x,y)=>x+(y-x+1)*crypto.getRandomValues(new Uint32Array(1))[0]/2**32|0
#aDiv,#bDiv{
color:yellow;
background-color:black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="maindiv">
<div id="aDiv">
</div>
<div id="bDiv" style="margin-top:50px;">
</div>
</div>
Take the following code:
<div id="work">
<div class="large-{{columns}} large-offset-{{columns}} columns projects">
</div>
</div>
The idea is that <div class="large-{{columns}} large-offset-{{columns}} columns projects"> can be generated an indefinite amount of times inside #work, and {{columns}} generates a number between 0 and 12.
What I want to do is run some JavaScript that goes through the numbers generated by {{columns}} and every time the sum is about to surpass 12, the associated divs get wrapped inside a new div with class "row".
The resulting HTML might look like this:
<div id="work">
<div class="row">
<div class="large-8 large-offset-4 columns projects"></div>
</div>
<div class="row">
<div class="large-6 large-offset-0 columns projects></div>
<div class="large-6 large-offset-0 columns projects"></div>
</div>
<div class="row">
<div class="large-4 large-offset-0 columns projects"></div>
<div class="large-8 large-offset-0 columns projects"></div>
</div>
<div class="row">
<div class="large-12 large-offset-0 columns projects"></div>
</div>
</div>
How can I accomplish this?
You can extract the {{columns}} values from each div's class name with the following regular expression:
/large-(\d+)\s* large-offset-(\d+)/
This computes the delta that should be added to the running sum:
var matches = /large-(\d+)\s* large-offset-(\d+)/.exec(item.className),
delta = parseInt(matches[1], 10) + parseInt(matches[2], 10);
You can make new row divs with document.createElement and fill them with clones of the original divs.
Demonstration:
function makeRowDiv(buildRow) {
var row = document.createElement('div');
row.className = 'row';
for (var i = 0; i < buildRow.length; ++i) {
row.appendChild(buildRow[i]);
}
return row;
}
window.onload = function () {
var work = document.getElementById('work'),
items = work.getElementsByTagName('div'),
newWork = document.createElement('div');
var buildRow = [],
count = 0;
for (var i = 0; i < items.length; ++i) {
var item = items[i];
if (item.className.indexOf('columns') == -1) {
continue;
}
// Extract the desired value.
var matches = /large-(\d+)\s* large-offset-(\d+)/.exec(item.className),
delta = parseInt(matches[1], 10) + parseInt(matches[2], 10);
if (count + delta > 12 && buildRow.length != 0) {
newWork.appendChild(makeRowDiv(buildRow));
count = 0;
buildRow = [];
}
buildRow.push(item.cloneNode(true));
count += delta;
}
if (buildRow.length != 0) {
newWork.appendChild(makeRowDiv(buildRow));
}
// Replace work with newWork.
work.parentNode.insertBefore(newWork, work);
work.parentNode.removeChild(work);
newWork.id = 'work';
};
body {
font-family: sans-serif;
font-size: 14px;
color: #444;
}
#work .row {
padding: 1px;
margin: 8px;
background: #deedff;
border: 1px solid #c4d1e1;
}
#work .row div {
/* display: inline; */
padding: 1px 4px 2px 4px;
margin: 4px;
background: #fff3fc;
border: 1px solid #ded3dc;
}
#work .row div div {
/* display: inline; */
padding: 1px 4px 2px 4px;
margin: 4px;
background: #eee;
border: 1px solid #ddd;
}
p {
padding: 0;
margin: 0;
}
<div id="work">
<div class="large-8 large-offset-4 columns projects">
<div class="child-div"><p>8</p></div>
<div class="child-div"><p>4</p></div>
</div>
<div class="large-6 large-offset-0 columns projects">
<div class="child-div"><p>6</p></div>
</div>
<div class="large-3 large-offset-3 columns projects">
<div class="child-div"><p>3</p></div>
<div class="child-div"><p>3</p></div>
</div>
<div class="large-4 large-offset-0 columns projects">
<div class="child-div"><p>4</p></div>
</div>
<div class="large-8 large-offset-0 columns projects">
<div class="child-div"><p>8</p></div>
</div>
<div class="large-6 large-offset-6 columns projects">
<div class="child-div"><p>6</p></div>
<div class="child-div"><p>6</p></div>
</div>
</div>
If you have enough horizontal space, you can uncomment the CSS line /* display: inline; */ to see the children of each row div arranged side by side.
I would use split or replace to get your integers and sum them up as suggested here.
Example:
var str = 'large-8 large-offset-6';
var large = str.replace(/.*large-(\d+)/, '$1');
var offset = str.replace(/.*large-offset-(\d+)/, '$1');
Then use a solution such as this to get your wrappers.
Example:
var divs = $("#work > .columns");
var count = <count how many cols are need to reach sum>
for(var i = 0; i < divs.length; i+=count) {
divs.slice(i, i+count).wrapAll("<div class='new'></div>");
}
I'm sure you can clean it up and finish it off but should give you the idea. I will complete when I get time tonight.