I have made a tic tac toe using JS however it has some problems My code is:
let x = [];
let o = [];
let xTurn = false;
let winPat = [
['00', '10', '20'],
['01', '11', '21'],
['02', '12', '22'],
['00', '01', '02'],
['10', '11', '12'],
['20', '21', '22'],
['00', '11', '22'],
['02', '11', '20']
];
const tableR = document.getElementsByClassName('tableR');
const button = document.getElementById('btn')
for (let i = 0; i <= 2; i++) {
for (let j = 0; j <= 2; j++) {
let newElement = document.createElement('td');
tableR.item(i).appendChild(newElement);
newElement.addEventListener('click', () => {
if (elementIsEmpty(newElement)) {
const img = document.createElement('img');
if (xTurn) {
img.src = 'https://www.google.com/url?sa=i&url=https%3A%2F%2Fcommons.wikimedia.org%2Fwiki%2FFile%3ARed_X.svg&psig=AOvVaw1B-fppvlN2x5oSCGllnXGc&ust=1634565535566000&source=images&cd=vfe&ved=0CAkQjRxqFwoTCMjbg6XN0fMCFQAAAAAdAAAAABAD';
x.push(String(i) + String(j))
} else {
img.src = 'https://www.google.com/imgres?imgurl=https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2Fd%2Fd0%2FLetter_o.svg%2F407px-Letter_o.svg.png&imgrefurl=https%3A%2F%2Fcommons.wikimedia.org%2Fwiki%2FFile%3ALetter_o.svg&tbnid=p-B77Lz3DDttwM&vet=12ahUKEwizlYTWzdHzAhXMg9gFHTSaBJkQMygAegUIARDaAQ..i&docid=K2HPZsIMOu4d5M&w=407&h=768&q=pixture%20of%20o&ved=2ahUKEwizlYTWzdHzAhXMg9gFHTSaBJkQMygAegUIARDaAQ';
o.push(String(i) + String(j))
}
newElement.append(img)
checkWinOrDraw(xTurn)
xTurn = !xTurn
}
})
}
}
const td = document.getElementsByTagName('td')
button.addEventListener('click', () => {
reset();
});
function elementIsEmpty(el) {
return (/^(\s| )*$/.test(el.innerHTML));
}
function checkWinOrDraw(xTurn) {
for (let i = 0; i < winPat.length; i++) {
if (xTurn) {
if (winPat[i].every(element => x.includes(element))) {
alert('X wins')
reset()
return
}
} else {
if (winPat[i].every(element => o.includes(element))) {
alert('O wins')
reset()
return
}
}
}
for (let item of td) {
if (elementIsEmpty(item))
return
}
alert('Draw')
reset()
}
function reset() {
x = [];
o = [];
for (let item of td) {
item.textContent = ''
}
}
body {
margin: 0px;
background-color: #3c4552;
color: aliceblue;
text-align: center;
}
header {
height: 75px;
background-color: #1f1e1c;
padding: 20px;
font-size: large;
}
table {
border-collapse: collapse;
margin: 40px auto;
}
td {
border: 7px solid black;
height: 121px;
width: 121px;
cursor: pointer;
}
button {
background-color: #1f1e1c;
color: white;
width: 25%;
height: 50px;
font-size: larger;
border: black solid 2px;
border-radius: 7px;
cursor: pointer;
}
img {
display: block;
width: 100%;
height: 100%;
}
<header>
<h1>TicTacToe</h1>
</header>
<main>
<table>
<tr class="tableR"></tr>
<tr class="tableR"></tr>
<tr class="tableR"></tr>
</table>
</main>
<footer>
<button id="btn">RESET</button>
</footer>
my problem is that when the game ends, say X wins, the image is not rendered on the screen, and alert() is called. I googled a lot and found no solutions.
details and clarity:
the image is not rendered on screen at the end of the game. try on your computer and see. alert() is called before the image is present in-game which results in a bad user experience. hence I need a solution.
Here your append() function is not able to complete image load & your checkWinOrDraw() function got called that's why this is happening.
Please try with the below code i am calling the checkWinOrDraw() once image gets loaded or failed.
for (let i = 0; i <= 2; i++) {
for (let j = 0; j <= 2; j++) {
let newElement = document.createElement('td');
tableR.item(i).appendChild(newElement);
newElement.addEventListener('click', () => {
if (elementIsEmpty(newElement)) {
const img = document.createElement('img');
if (xTurn) {
img.src = 'https://dummyimage.com/20x20/3a4ca6/fff';
x.push(String(i) + String(j))
} else {
img.src = 'https://dummyimage.com/30x30/fff/fff';
o.push(String(i) + String(j))
}
console.log(xTurn)
xTurn = !xTurn
img.addEventListener('load', checkWinOrDraw)
img.addEventListener('error', checkWinOrDraw)
newElement.appendChild(img)
}
})
}
}
function checkWinOrDraw(e,xTurn) {
//console.log('checking',xTurn)
for (let i = 0; i < winPat.length; i++) {
if (xTurn) {
if (winPat[i].every(element => x.includes(element))) {
alert('X wins')
reset()
return
}
} else {
if (winPat[i].every(element => o.includes(element))) {
alert('O wins')
reset()
return
}
}
}
for (let item of td) {
if (elementIsEmpty(item))
return
}
alert('Draw')
reset()
}
This Javascript function allows for multiple file uploads and includes a file size limit, however it is missing a maximum number of files allowed to be uploaded.
The function handleFile(e) has the file type and size arguments passed through it but do not know where to introduce a limit on the allowable number of files to be uploaded.
const fInputs = document.querySelectorAll('.btcd-f-input>div>input')
function getFileSize(size) {
let _size = size
let unt = ['Bytes', 'KB', 'MB', 'GB'],
i = 0; while (_size > 900) { _size /= 1024; i++; }
return (Math.round(_size * 100) / 100) + ' ' + unt[i];
}
function delItem(el) {
fileList = { files: [] }
let fInp = el.parentNode.parentNode.parentNode.querySelector('input[type="file"]')
for (let i = 0; i < fInp.files.length; i++) {
fileList.files.push(fInp.files[i])
}
fileList.files.splice(el.getAttribute('data-index'), 1)
fInp.files = createFileList(...fileList.files)
if (fInp.files.length > 0) {
el.parentNode.parentNode.parentNode.querySelector('.btcd-f-title').innerHTML = `${fInp.files.length} File Selected`
} else {
el.parentNode.parentNode.parentNode.querySelector('.btcd-f-title').innerHTML = 'No File Chosen'
}
el.parentNode.remove()
}
function fade(element) {
let op = 1; // initial opacity
let timer = setInterval(function () {
if (op <= 0.1) {
clearInterval(timer);
element.style.display = 'none';
}
element.style.opacity = op;
element.style.filter = 'alpha(opacity=' + op * 100 + ")";
op -= op * 0.1;
}, 50);
}
function unfade(element) {
let op = 0.01; // initial opacity
element.style.opacity = op;
element.style.display = 'flex';
let timer = setInterval(function () {
if (op >= 1) {
clearInterval(timer);
}
element.style.opacity = op;
element.style.filter = 'alpha(opacity=' + op * 100 + ")";
op += op * 0.1;
}, 13);
}
function get_browser() {
let ua = navigator.userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
if (/trident/i.test(M[1])) {
tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
return { name: 'IE', version: (tem[1] || '') };
}
if (M[1] === 'Chrome') {
tem = ua.match(/\bOPR|Edge\/(\d+)/)
if (tem != null) { return { name: 'Opera', version: tem[1] }; }
}
M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
if ((tem = ua.match(/version\/(\d+)/i)) != null) { M.splice(1, 1, tem[1]); }
return {
name: M[0],
version: M[1]
};
}
for (let inp of fInputs) {
inp.parentNode.querySelector('.btcd-inpBtn>img').src = ''
inp.addEventListener('mousedown', function (e) { setPrevData(e) })
inp.addEventListener('change', function (e) { handleFile(e) })
}
let fileList = { files: [] }
let fName = null
let mxSiz = null
function setPrevData(e) {
if (e.target.hasAttribute('multiple') && fName !== e.target.name) {
console.log('multiple')
fName = e.target.name
fileList = fileList = { files: [] }
if (e.target.files.length > 0) {
for (let i = 0; i < e.target.files.length; i += 1) {
console.log(e.target.files[i])
fileList.files.push(e.target.files[i])
}
}
}
}
function handleFile(e) {
let err = []
const fLen = e.target.files.length;
mxSiz = e.target.parentNode.querySelector('.f-max')
mxSiz = mxSiz != null && (Number(mxSiz.innerHTML.replace(/\D/g, '')) * Math.pow(1024, 2))
if (e.target.hasAttribute('multiple')) {
for (let i = 0; i < fLen; i += 1) {
fileList.files.push(e.target.files[i])
}
} else {
fileList.files.push(e.target.files[0])
}
//type validate
if (e.target.hasAttribute('accept')) {
let tmpf = []
let type = new RegExp(e.target.getAttribute('accept').split(",").join("$|") + '$', 'gi')
for (let i = 0; i < fileList.files.length; i += 1) {
if (fileList.files[i].name.match(type)) {
tmpf.push(fileList.files[i])
} else {
err.push('Wrong File Type Selected')
}
}
fileList.files = tmpf
}
// size validate
if (mxSiz > 0) {
let tmpf = []
for (let i = 0; i < fileList.files.length; i += 1) {
if (fileList.files[i].size < mxSiz) {
tmpf.push(fileList.files[i])
mxSiz -= fileList.files[i].size
} else {
console.log('rejected', i, fileList.files[i].size)
err.push('Max Upload Size Exceeded')
}
}
fileList.files = tmpf
}
if (e.target.hasAttribute('multiple')) {
e.target.files = createFileList(...fileList.files)
} else {
e.target.files = createFileList(fileList.files[fileList.files.length - 1])
fileList = { files: [] }
}
// set File list view
if (e.target.files.length > 0) {
e.target.parentNode.querySelector('.btcd-f-title').innerHTML = e.target.files.length + ' File Selected'
e.target.parentNode.parentNode.querySelector('.btcd-files').innerHTML = ''
for (let i = 0; i < e.target.files.length; i += 1) {
let img = null
if (e.target.files[i].type.match(/image-*/)) {
img = window.URL.createObjectURL(e.target.files[i])
}
else {
img = ''
}
e.target.parentNode.parentNode.querySelector('.btcd-files').insertAdjacentHTML('beforeend', `<div>
<img src="${img}" alt="img" title="${e.target.files[i].name}">
<div>
<span title="${e.target.files[i].name}">${e.target.files[i].name}</span>
<br/>
<small>${getFileSize(e.target.files[i].size)}</small>
</div>
<button type="button" onclick="delItem(this)" data-index="${i}" title="Remove This File"><span>×</span></button>
</div>`)
}
}
// set eror
if (err.length > 0) {
for (let i = 0; i < err.length; i += 1) {
e.target.parentNode.parentNode.querySelector('.btcd-files').insertAdjacentHTML('afterbegin', `
<div style="background: #fff2f2;color: darkred;display:none" class="btcd-f-err">
<img src="" alt="img">
<span>${err[i]}</span>
</div>`)
}
const errNods = e.target.parentNode.parentNode.querySelectorAll('.btcd-files>.btcd-f-err')
for (let i = 0; i < errNods.length; i += 1) {
unfade(errNods[i])
setTimeout(() => { fade(errNods[i]) }, 3000);
setTimeout(() => { errNods[i].remove() }, 4000);
}
err = []
}
}
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-family: Arial, Helvetica, sans-serif;
}
.btcd-f-input {
display: inline-block;
width: 340px;
position: relative;
overflow: hidden;
}
.btcd-f-input>div>input::-webkit-file-upload-button {
cursor: pointer;
}
.btcd-f-wrp {
cursor: pointer;
}
.btcd-f-wrp>small {
color: gray;
}
.btcd-f-wrp>button {
cursor: pointer;
background: #f3f3f3;
padding: 5px;
display: inline-block;
border-radius: 9px;
border: none;
margin-right: 8px;
height: 35px;
}
.btcd-f-wrp>button>img {
width: 24px;
}
.btcd-f-wrp>button>span,
.btcd-f-wrp>span,
.btcd-f-wrp>small {
vertical-align: super;
}
.btcd-f-input>.btcd-f-wrp>input {
z-index: 100;
width: 100%;
position: absolute;
opacity: 0;
left: 0;
height: 37px;
cursor: pointer;
}
.btcd-f-wrp:hover {
background: #fafafa;
border-radius: 10px;
}
.btcd-files>div {
display: flex;
align-items: center;
background: #f8f8f8;
border-radius: 10px;
margin-left: 30px;
width: 91%;
margin-top: 10px;
height: 40px;
}
.btcd-files>div>div {
display: inline-block;
width: 73%;
}
.btcd-files>div>div>small {
color: gray;
}
.btcd-files>div>img {
width: 40px;
height: 40px;
margin-right: 10px;
border-radius: 10px;
}
.btcd-files>div>div>span {
display: inline-block;
width: 100%;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.btcd-files>div>button {
background: #e8e8e8;
border: none;
border-radius: 50px;
width: 25px;
height: 25px;
font-size: 20px;
margin-right: 6px;
padding: 0;
}
.btcd-files>div>button:hover {
background: #bbbbbb;
}
<div class="btcd-f-input">
<small>Multiple Upload</small>
<div class="btcd-f-wrp">
<button class="btcd-inpBtn" type="button"> <img src="" alt=""> <span> Attach File</span></button>
<span class="btcd-f-title">No File Chosen</span>
<small class="f-max">(Max 1 MB)</small>
<input multiple type="file" name="snd_multiple" id="">
</div>
<div class="btcd-files">
</div>
</div>
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
<script src="https://unpkg.com/create-file-list#1.0.1/dist/create-file-list.min.js"></script>
In the function handleFile before type validate:
let maxFileNum = 10; // Maximum number of files
if (fileList.files.length > maxFileNum){
err.push("Too many files selected");
}
I making a swipable slider using touch events, when remains one visible element of visible elements I want to load new elements, for that I'm using this method:
initInteractionWithItems(elementsToMakeVisible) {
let totalMargin = 0 //the items are stacked and in the loop, the margin gradually gets bigger
let indexZ = 0 //the indexZ also gets bigger in the loop (is the z-index for the stacked elems)
const sizeItems = this.items.length
let styleElement = document.createElement("style")
document.head.appendChild(styleElement)
if (elementsToMakeVisible) {
firstInTheList.style.zIndex = "" + (elementsToMakeVisible + 1) * 1000
} else {
elementsToMakeVisible = this.itemsVisibleLength
}
totalMargin = this.marginTopFactor * elementsToMakeVisible
indexZ = elementsToMakeVisible * 1000
//while the list have elements, I show a portion of those
while ((elementsToMakeVisible !== 0) && (this.elementsCount < sizeItems)) {
let rule = ".swipeItem-" + this.elementsCount + "{ opacity: " + "1;" + "margin-top:" + totalMargin + "px;" + "z-index:" + indexZ + ";}"
this.items[this.elementsCount].classList.add("swipeItem-" + this.elementsCount)
totalMargin -= this.marginTopFactor
indexZ -= 1000
elementsToMakeVisible--
this.elementsCount++
styleElement.sheet.insertRule(rule)
}
}
}
The method do the job of show the elements, this same method I use when the page loads and I need
to show the first elements. but when I have to show more elements when the slider have only one element
the page gets slow, the element I removed (because I swipe it) gets stuck and lates to hide totally. finally the elements that I want load are show.
When I have to delete an element because was selected I use this method:
function sendTarget(event) {
if (event.cancelable) {
canSlide = false
event.preventDefault()
if (!has_option_selected) { //if a element has been selected
event.target.offsetParent.style.transform = "rotateZ(0deg)"
} else {
firstInTheList.remove()
firstInTheList = iteratorItems.next().value
countElements--
if (countElements === 1) { //if in the slider remains only one element
swipeReference.initInteractionWithItems(2)
}
}
}
}
Only the first element have the touch events, and every time an element gets selected I removed that element and the first element will be the next in the array I use for have the elements I want to show, for that I use this generator function:
function* getFirstItem(arrayElements) {
for (let i = 0; i < arrayElements.length; i++) {
arrayElements[i].addEventListener("touchstart", getCoords, true)
arrayElements[i].addEventListener("touchmove", slideTarget, true)
arrayElements[i].addEventListener("touchend", sendTarget, true)
yield arrayElements[i]
}
}
When I remove an element and I don't have to show new elements the sendTarget method works fine. the trouble is when I have to show new elements and I have to inkove initInteractionWithItems again.
How can I fix that?, what approach can I do for solve this performance trouble?
The full project:
let has_option_selected = false
let canSlide = false
let firstTime = true
let current_coord_x
let current_coord_y
let firstInTheList = null
let swipeItsClassName
let countElements = 3
let swipeReference = null
let iteratorItems = null
function getCoords(event) {
if (event.target.offsetParent.classList.contains("swipeItem") && event.cancelable) {
event.preventDefault()
canSlide = true
current_coord_x = event.changedTouches[0].clientX
current_coord_y = event.changedTouches[0].clientY
} else {
current_coord_x = event.clientX
current_coord_y = event.clientY
}
}
function calcRotation(translateXValue, element) {
let rotateValue = translateXValue * 0.10
if (rotateValue <= -15) {
notifySelection(element, 1)
return -15
} else if (rotateValue >= 15) {
notifySelection(element, -1)
return 15
} else {
notifySelection(element, 0)
return rotateValue
}
}
function moveTarget(event, target) {
let translateX = (event.changedTouches[0].clientX - current_coord_x)
let translateY = (event.changedTouches[0].clientY - current_coord_y)
if (event instanceof TouchEvent) {
if (firstTime) {
translateY += 100
}
target.style.transform = "translateX(" + translateX + "px" + ")" + " " + "translateY(" + translateY + "px)" + " " + "rotateZ(" + calcRotation(translateX, target) + "deg)"
}
}
function notifySelection(target, state) {
if (state === 1) {
//target.offsetParent.style.color = "green"
has_option_selected = true
} else if (state === -1) {
//target.offsetParent.style.color = "red"
has_option_selected = true
} else {
// target.offsetParent.style.color = "yellow"
has_option_selected = false
}
}
function sendTarget(event) {
if (event.cancelable) {
canSlide = false
event.preventDefault()
if (!has_option_selected) {
event.target.offsetParent.style.transform = "rotateZ(0deg)"
} else {
firstInTheList.remove()
firstInTheList = iteratorItems.next().value
countElements--
if (countElements === 1) {
swipeReference.initInteractionWithItems(2)
}
}
}
}
function slideTarget(e) {
let parentTarget = e.target.offsetParent
let hasClassItem = parentTarget.classList.contains("swipeItem")
if ((canSlide && e.cancelable && hasClassItem) && (firstInTheList === parentTarget)) {
e.preventDefault()
moveTarget(e, parentTarget)
}
}
function* getFirstItem(arrayElements) {
for (let i = 0; i < arrayElements.length; i++) {
arrayElements[i].addEventListener("touchstart", getCoords, true)
arrayElements[i].addEventListener("touchmove", slideTarget, true)
arrayElements[i].addEventListener("touchend", sendTarget, true)
yield arrayElements[i]
}
}
class SwipeSlider {
constructor(swipeItems, marginTopFactor = 30, itemsVisibleLenght = 3) {
//swipeItsClassName = swipeItemsClassName
swipeReference = this
this.items = swipeItems
iteratorItems = getFirstItem(this.items)
firstInTheList = iteratorItems.next().value
this.itemsVisibleLength = itemsVisibleLenght
this.marginTopFactor = marginTopFactor
this.totalMarginSize = (this.marginTopFactor * itemsVisibleLenght)
this.elementsCount = 0
}
initInteractionWithItems(elementsToMakeVisible) {
let totalMargin = 0
let indexZ = 0
const sizeItems = this.items.length
let styleElement = document.createElement("style")
document.head.appendChild(styleElement)
if (elementsToMakeVisible) {
firstInTheList.style.zIndex = "" + (elementsToMakeVisible + 1) * 1000
} else {
elementsToMakeVisible = this.itemsVisibleLength
}
totalMargin = this.marginTopFactor * elementsToMakeVisible
indexZ = elementsToMakeVisible * 1000
while ((elementsToMakeVisible !== 0) && (this.elementsCount < sizeItems)) {
let rule = ".swipeItem-" + this.elementsCount + "{ opacity: " + "1;" + "margin-top:" + totalMargin + "px;" + "z-index:" + indexZ + ";}"
this.items[this.elementsCount].classList.add("swipeItem-" + this.elementsCount)
totalMargin -= this.marginTopFactor
indexZ -= 1000
elementsToMakeVisible--
this.elementsCount++
styleElement.sheet.insertRule(rule)
}
}
}
function getArrayElements(elements) {
let array = []
for (let i = 0; i < 6; i++) {
array.push(elements[i])
}
return array
}
let arrayElements = getArrayElements(document.querySelectorAll(".swipeItem"))
let slider = new SwipeSlider(arrayElements)
slider.initInteractionWithItems()
#import url('https://fonts.googleapis.com/css?family=Solway&display=swap');
:root{
--fontHeadingProspect: 25px;
}
*{
box-sizing: border-box;
padding: 0;
margin: 0;
}
.swipeImg{
width: 100%;
height: 300px;
grid-row: 1;
grid-column: 1;
object-fit: cover;
object-position: center;
border-radius: 15px;
}
.swipeContainer{
position: relative;
display: grid;
min-height: 100vh;
justify-content: center;
align-content: center;
align-items: center;
justify-items: center;
grid-template-rows: minmax(300px,auto);
}
.gridSwipeItem{
display: grid;
}
.swipeItem{
background-color: rgb(252, 237, 223);
position: relative;
max-width: 270px;
grid-column: 1 ;
grid-row: 1;
opacity: 0;
font-family: 'Solway', serif;
box-shadow: 0px 0px 5px rgb(233, 209, 130);
border-radius: 15px;
transition: opacity 0.5s;
z-index: 0;
}
.headingProspect{
grid-row: 1;
grid-column: 1;
font-size: var(--fontHeadingProspect);
color: white;
z-index: 1;
align-self: end;
margin-left: 15px;
margin-bottom: 15px;
}
.headingProspectSection{
writing-mode: vertical-rl;
grid-column: 1 / 2;
justify-self: start;
transform: rotateZ(180deg);
margin-left: 30px;
font-size: 34px;
font-family: 'Solway', serif;
text-align: center;
text-transform: uppercase;
color: #f74040;
}
.selection-zone{
background-image: rgb(255, 225, 169);
}
.visibleTarget{
opacity: 1;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="style.css">
<title>Document</title>
</head>
<body>
<section class="selection-zone">
<div class="swipeContainer" id="swipeContainer">
<article class="swipeItem gridSwipeItem" id="swipeItem">
<img src="http://lorempixel.com/1200/1200" class="swipeImg">
<h1 id="prospectState" class="headingProspect">Rose Ann, 19</h1>
</article>
<article class="swipeItem gridSwipeItem">
<img src="http://lorempixel.com/1200/1200" class="swipeImg">
<h1 id="prospectState" class="headingProspect">Mary Wolfstoncraft, 24</h1>
</article>
<article class="swipeItem gridSwipeItem">
<img src="http://lorempixel.com/1200/1200" class="swipeImg">
<h1 id="prospectState" class="headingProspect">Rose Doe, 34</h1>
</article>
<article class="swipeItem gridSwipeItem">
<img src="http://lorempixel.com/1200/1200" class="swipeImg">
<h1 id="prospectState" class="headingProspect">Joane Smith, 34</h1>
</article>
<article class="swipeItem gridSwipeItem">
<img src="http://lorempixel.com/1200/1200" class="swipeImg">
<h1 id="prospectState" class="headingProspect">Mary Wolfstoncraft, 24</h1>
</article>
</div>
</section>
<script src="script.js"></script>
</body>
</html>
I want to split the sentence when i click on the space. He has to make 2 parts of the sentence. I want that i click another time on a space and then to make 3 parts of the sentence.
I've tried to search on google, stackoverflow, etc. But i don't see my answer.
So this is my code.
$(function() {
$(document).data("text", $("#editor").text())
.on("mousedown", function() {
$("#editor")
.html($(this)
.data("text"))
})
.on("mouseup", function() {
that = $("#editor");
var sel = window.getSelection ? window.getSelection() : document.selection();
var o = that.text();
var before = sel.baseOffset;
var after = o.length - before;
var a = o.slice(0, before);
var b = after === 0 ? "" : o.slice(-after);
var n = "<data>||</data>";
var html = (after === "" ? a + n : a + n + b);
that.html(html);
});
})
#editor {
font-family: Sans;
font-size: 28px;
letter-spacing: 8px;
white-space: pre;
}
#editor > data {
color: red;
max-width: .1em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="editor">Hello! I am a Text!</div>
So i hope i've i have the sentence: Hello this is a test. And then click between 'is' and 'a':
Hello this is a test.
$(function() {
jQuery.fn.reverse = [].reverse;
// Add element around all characters
var text = $("#editor").text();
var newText = "";
for (var i = 0; i < text.length; i++) {
newText += `<span>${text[i]}</span>`;
}
$("#editor").html(newText);
// If you click on a space
$("#editor").on("click", "span", function() {
if ($(this).text() == " ") {
var before = $(this).prevAll().reverse();
var after = $(this).nextAll()
$("#editor").html("");
before.each(function() {
if (!$(this).hasClass("white")) {
$("#editor").append(`<span class="yellow">${$(this).text()}</span>`);
} else {
$("#editor").append(`<span class="white">${$(this).text()}</span>`);
}
});
$("#editor").append(`<span class="white"> </span>`);
after.each(function() {
if (!$(this).hasClass("white")) {
$("#editor").append(`<span class="yellow">${$(this).text()}</span>`);
} else {
$("#editor").append(`<span class="white">${$(this).text()}</span>`);
}
});
}
});
})
#editor {
font-family: Sans;
font-size: 28px;
letter-spacing: 8px;
white-space: pre;
}
#editor .br {
color: red;
max-width: .1em;
}
#editor .yellow {
background-color: yellow;
}
#editor .white {
background-color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="editor">Hello! I am a Text</div>
--- Old answer below
This should be a good start. You can do whatever you want with the variables before and after - so that is suits your case.
$(function() {
jQuery.fn.reverse = [].reverse;
// Add element around all characters
var text = $("#editor").text();
var newText = "";
for (var i = 0; i < text.length; i++) {
newText += `<span>${text[i]}</span>`;
}
$("#editor").html(newText);
// If you click on a space
$("#editor").on("click", "span", function() {
if ($(this).text() == " ") {
var before = $(this).prevAll().reverse();
var after = $(this).nextAll()
$("#editor").html("");
before.each(function() {
$("#editor").append(`<span>${$(this).text()}</span>`);
});
$("#editor").append(`<span class="br">|</span>`);
after.each(function() {
$("#editor").append(`<span>${$(this).text()}</span>`);
});
}
});
})
#editor {
font-family: Sans;
font-size: 28px;
letter-spacing: 8px;
white-space: pre;
}
#editor .br {
color: red;
max-width: .1em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="editor">Hello! I am a Text</div>
I have this code im using to display events from a calendar but its displaying ALL of the events for that month and I only want to display the events for the day when a user clicks on a certain day. For example, click on February 10, you get all of the events for that day.
var eventList = $('<div/>').addClass('c-event-list');
for (var i = 0; i < settings.events.length; i++) {
var d = settings.events[i].datetime;
if ((d.getMonth() - 1) == dMonth && d.getFullYear() == dYear) {
var date = lpad(d.getMonth(), 2) + '/' +
lpad(d.getDate(), 2) + ' ' +
lpad(d.getHours() %12, 2) + ':' +
lpad(d.getMinutes(), 2);
var item = $('<div/>').addClass('c-event-item');
var title = $('<div/>')
.addClass('title')
.html(date + ' ' + settings.events[i].title + '<br />');
var description = $('<div/>')
.addClass('description')
.html(settings.events[i].description + '<br />');
item.attr('data-event-day', d.getDate());
item.on('mouseover', mouseOverItem).on('mouseleave', mouseLeaveItem);
item.append(title).append(description);
eventList.append(item);
}
}
$(instance).addClass('calendar');
cEventsBody.append(eventList);
$(instance).html(cBody).append(cEvents);
}
return print();
}
Thanks in advance for any assistance.
What I can see from your code is that this condition determines the month and year that events should be held on:
((d.getMonth() - 1) == dMonth && d.getFullYear() == dYear)
So when you want to display events only on a certain day you have to extend this condition like:
(d.getDate() == dDay && (d.getMonth() - 1) == dMonth && d.getFullYear() == dYear)
dDay is a new varibale that holds the day that events should be held on.
Following your request within last answer to show "no events" when a month is selected and there are none available.
I have modified this plugin so that it shows a "text"(for instance "no events") when the list on the right side is empty. I have tested it and it works.
This modification is ok because it is MIT licensed.
(function($) {
var eCalendar = function(options, object) {
// Initializing global variables
var adDay = new Date().getDate();
var adMonth = new Date().getMonth();
var adYear = new Date().getFullYear();
var dDay = adDay;
var dMonth = adMonth;
var dYear = adYear;
var instance = object;
var settings = $.extend({}, $.fn.eCalendar.defaults, options);
function lpad(value, length, pad) {
if (typeof pad == 'undefined') {
pad = '0';
}
var p;
for (var i = 0; i < length; i++) {
p += pad;
}
return (p + value).slice(-length);
}
var mouseOver = function() {
$(this).addClass('c-nav-btn-over');
};
var mouseLeave = function() {
$(this).removeClass('c-nav-btn-over');
};
var mouseOverEvent = function() {
$(this).addClass('c-event-over');
var d = $(this).attr('data-event-day');
$('div.c-event-item[data-event-day="' + d + '"]').addClass('c-event-over');
};
var mouseLeaveEvent = function() {
$(this).removeClass('c-event-over')
var d = $(this).attr('data-event-day');
$('div.c-event-item[data-event-day="' + d + '"]').removeClass('c-event-over');
};
var mouseOverItem = function() {
$(this).addClass('c-event-over');
var d = $(this).attr('data-event-day');
$('div.c-event[data-event-day="' + d + '"]').addClass('c-event-over');
};
var mouseLeaveItem = function() {
$(this).removeClass('c-event-over')
var d = $(this).attr('data-event-day');
$('div.c-event[data-event-day="' + d + '"]').removeClass('c-event-over');
};
var nextMonth = function() {
if (dMonth < 11) {
dMonth++;
} else {
dMonth = 0;
dYear++;
}
print();
};
var previousMonth = function() {
if (dMonth > 0) {
dMonth--;
} else {
dMonth = 11;
dYear--;
}
print();
};
var checkEventsOnCurrentMonth = function() {
var eventNum = $('.c-event-list').find('.c-event-item') ? $('.c-event-list').find('.c-event-item').length : 0;
if (!eventNum) {
$('.c-event-list').html($.fn.eCalendar.defaults.noEventText);
}
};
function loadEvents() {
if (typeof settings.url != 'undefined' && settings.url != '') {
$.ajax({
url: settings.url,
async: false,
success: function(result) {
settings.events = result;
}
});
}
}
function print() {
loadEvents();
var dWeekDayOfMonthStart = new Date(dYear, dMonth, 1).getDay();
var dLastDayOfMonth = new Date(dYear, dMonth + 1, 0).getDate();
var dLastDayOfPreviousMonth = new Date(dYear, dMonth + 1, 0).getDate() - dWeekDayOfMonthStart + 1;
var cBody = $('<div/>').addClass('c-grid');
var cEvents = $('<div/>').addClass('c-event-grid');
var cEventsBody = $('<div/>').addClass('c-event-body');
cEvents.append($('<div/>').addClass('c-event-title c-pad-top').html(settings.eventTitle));
cEvents.append(cEventsBody);
var cNext = $('<div/>').addClass('c-next c-grid-title c-pad-top');
var cMonth = $('<div/>').addClass('c-month c-grid-title c-pad-top');
var cPrevious = $('<div/>').addClass('c-previous c-grid-title c-pad-top');
cPrevious.html(settings.textArrows.previous);
cMonth.html(settings.months[dMonth] + ' ' + dYear);
cNext.html(settings.textArrows.next);
cPrevious.on('mouseover', mouseOver).on('mouseleave', mouseLeave).on('click', previousMonth);
cNext.on('mouseover', mouseOver).on('mouseleave', mouseLeave).on('click', nextMonth);
cBody.append(cPrevious);
cBody.append(cMonth);
cBody.append(cNext);
for (var i = 0; i < settings.weekDays.length; i++) {
var cWeekDay = $('<div/>').addClass('c-week-day c-pad-top');
cWeekDay.html(settings.weekDays[i]);
cBody.append(cWeekDay);
}
var day = 1;
var dayOfNextMonth = 1;
for (var i = 0; i < 42; i++) {
var cDay = $('<div/>');
if (i < dWeekDayOfMonthStart) {
cDay.addClass('c-day-previous-month c-pad-top');
cDay.html(dLastDayOfPreviousMonth++);
} else if (day <= dLastDayOfMonth) {
cDay.addClass('c-day c-pad-top');
if (day == dDay && adMonth == dMonth && adYear == dYear) {
cDay.addClass('c-today');
}
for (var j = 0; j < settings.events.length; j++) {
var d = settings.events[j].datetime;
if (d.getDate() == day && (d.getMonth() - 1) == dMonth && d.getFullYear() == dYear) {
cDay.addClass('c-event').attr('data-event-day', d.getDate());
cDay.on('mouseover', mouseOverEvent).on('mouseleave', mouseLeaveEvent);
}
}
cDay.html(day++);
} else {
cDay.addClass('c-day-next-month c-pad-top');
cDay.html(dayOfNextMonth++);
}
cBody.append(cDay);
}
var eventList = $('<div/>').addClass('c-event-list');
for (var i = 0; i < settings.events.length; i++) {
var d = settings.events[i].datetime;
if ((d.getMonth() - 1) == dMonth && d.getFullYear() == dYear) {
var date = lpad(d.getDate(), 2) + '/' + lpad(d.getMonth(), 2) + ' ' + lpad(d.getHours(), 2) + ':' + lpad(d.getMinutes(), 2);
var item = $('<div/>').addClass('c-event-item');
var title = $('<div/>').addClass('title').html(date + ' ' + settings.events[i].title + '<br/>');
var description = $('<div/>').addClass('description').html(settings.events[i].description + '<br/>');
item.attr('data-event-day', d.getDate());
item.on('mouseover', mouseOverItem).on('mouseleave', mouseLeaveItem);
item.append(title).append(description);
eventList.append(item);
}
}
$(instance).addClass('calendar');
cEventsBody.append(eventList);
$(instance).html(cBody).append(cEvents);
checkEventsOnCurrentMonth();
}
return print();
}
$.fn.eCalendar = function(oInit) {
return this.each(function() {
return eCalendar(oInit, $(this));
});
};
// plugin defaults
$.fn.eCalendar.defaults = {
weekDays: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab'],
months: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
textArrows: {
previous: '<',
next: '>'
},
eventTitle: 'Eventos',
noEventText: 'no events',
url: '',
events: [{
title: 'Brasil x Croácia',
description: 'Abertura da copa do mundo 2014',
datetime: new Date(2014, 6, 12, 17)
}, {
title: 'Brasil x México',
description: 'Segundo jogo da seleção brasileira',
datetime: new Date(2014, 6, 17, 16)
}, {
title: 'Brasil x Camarões',
description: 'Terceiro jogo da seleção brasileira',
datetime: new Date(2014, 6, 23, 16)
}]
};
}(jQuery));
.calendar * {
box-sizing: border-box;
font-family: Tahoma;
font-size: 14px;
}
.calendar-sm {
cursor: default;
width: 800px;
height: 370px;
}
.calendar {
cursor: default;
width: 600px;
height: 270px;
}
.calendar-sm .c-pad-top {
padding-top: 2%;
}
.calendar .c-pad-top {
padding-top: 3%;
}
.c-grid {
box-shadow: 2px 2px 5px #888888;
height: inherit;
}
.c-day {
width: 14.28%;
height: 13%;
background-color: #EFF4F9;
float: left;
text-align: center;
}
.c-day-previous-month {
width: 14.28%;
height: 13%;
background-color: #F9FBFD;
float: left;
text-align: center;
color: gray;
}
.c-day-next-month {
width: 14.28%;
height: 13%;
background-color: #F9FBFD;
float: left;
text-align: center;
color: gray;
}
.c-week-day {
width: 14.28%;
height: 10.38%;
background-color: rgb(145, 172, 203);
color: white;
float: left;
text-align: center;
font-weight: bold;
padding-top: 1%;
}
.c-next {
width: 12.5%;
height: 12%;
padding: 2% 2% 0 2%;
text-align: right;
cursor: pointer;
}
.c-previous {
width: 12.5%;
height: 12%;
padding: 2% 2% 0 2%;
text-align: left;
cursor: pointer;
}
.c-month {
width: 75%;
height: 12%;
text-align: center;
}
.c-nav-btn-over {
background-color: rgb(137, 163, 192) !important;
font-weight: bold;
}
.c-today {
background-color: #D8EAF1;
}
.c-event {
background-color: rgb(166, 166, 166);
color: white;
font-weight: bold;
cursor: pointer;
}
.c-grid {
float: left;
width: 50%;
}
.c-event-grid {
margin-left: 1px;
height: inherit;
width: 49%;
float: left;
box-shadow: 2px 2px 5px #888888;
}
.c-grid-title {
font-weight: bold;
float: left;
background-color: rgb(112, 145, 183);
color: white;
}
.c-event-title {
width: 100%;
height: 12%;
text-align: center;
font-weight: bold;
background-color: rgb(135, 155, 188);
color: white;
}
.c-event-body {
background-color: #EFF4F9;
height: 88.1%;
}
.c-event-list {
padding: 7 0 0 0;
overflow: auto;
height: 95%;
}
.c-event-item > .title {
font-weight: bold;
}
.c-event-item > div {
text-overflow: ellipsis;
width: inherit;
overflow: hidden;
white-space: nowrap;
}
.c-event-item {
padding-left: 10px;
margin-bottom: 10px;
}
.c-event-over {
background-color: lightgray;
font-weight: bold;
color: black;
}
.c-event-over > .description {
font-weight: normal;
}
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>jQuery e-calendar Plugin Demo</title>
<link href="http://www.jqueryscript.net/css/jquerysctipttop.css" rel="stylesheet" type="text/css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script type="text/javascript" src="../js/jquery.e-calendar.js"></script>
<script type="text/javascript" src="index.js"></script>
<link rel="stylesheet" href="../css/jquery.e-calendar.css" />
</head>
<body>
<div id="jquery-script-menu">
<div class="jquery-script-center">
<ul>
<li>Download This Plugin
</li>
<li>Back To jQueryScript.Net
</li>
</ul>
<div class="jquery-script-ads">
<script type="text/javascript">
<!--
google_ad_client = "ca-pub-2783044520727903";
/* jQuery_demo */
google_ad_slot = "2780937993";
google_ad_width = 728;
google_ad_height = 90;
//-->
</script>
<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<div class="jquery-script-clear"></div>
</div>
</div>
<h1 style="margin-top:150px;">jQuery e-calendar Plugin Demo</h1>
<div id="calendar"></div>
</body>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-36251023-1']);
_gaq.push(['_setDomainName', 'jqueryscript.net']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();
</script>
<script>
$(document).ready(function() {
$.fn.eCalendar.defaults.noEventText = '<br/>there are no events available';
$('#calendar').eCalendar({
weekDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
textArrows: {
previous: '<',
next: '>'
},
eventTitle: 'Events',
url: '',
events: [{
title: 'Event 1',
description: 'Description 1',
datetime: new Date(2014, 7, 15, 17)
}, {
title: 'Event 2',
description: 'Description 2',
datetime: new Date(2014, 7, 14, 16)
}, {
title: 'Event 3',
description: 'jQueryScript.Net',
datetime: new Date(2014, 7, 10, 16)
}]
});
});
</script>
</html>
This is the function that check whether the event-list is empty and show a text if it's true. Put this function after previousMonth-function or somewhere within the library:
var checkEventsOnCurrentMonth = function(){
var eventNum = $('.c-event-list').find('.c-event-item') ? $('.c-event-list').find('.c-event-item').length : 0;
if(!eventNum){
$('.c-event-list').html('no events');
}
};
This function you have to call at the end of print:
function print() {
...
...
...
$(instance).addClass('calendar');
cEventsBody.append(eventList);
$(instance).html(cBody).append(cEvents);
// this is the function you have to call at the end of print:
checkEventsOnCurrentMonth();
}
Furthermore you can define the text in another fashion.
There are default values or settings at the bottom of the library.
$.fn.eCalendar.defaults = {
weekDays: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab'],
...
...
noEventText: 'no events',
...
...
};
I've defined a new property "noEventText" to be used as text when no events are available.
The function checkEventsOnCurrentMonth has to be altered for this new default-value(noEventText) to be taken into consideration:
var checkEventsOnCurrentMonth = function(){
var eventNum = $('.c-event-list').find('.c-event-item') ? $('.c-event-list').find('.c-event-item').length : 0;
if(!eventNum){
$('.c-event-list').html($.fn.eCalendar.defaults.noEventText);
}
};
At the end you can define the text before the plugin has been started:
$(document).ready(function () {
// here you can define the text and put some html into it.
$.fn.eCalendar.defaults.noEventText = 'there are no events available';
$('#calendar').eCalendar({
...
...
});
});
As mentioned I've tested it but only with the download-version you can find here: http://www.jqueryscript.net/time-clock/Create-A-Simple-Event-Calendar-with-jQuery-e-calendar.html
Image: