I'm still beginner with CSS and Javascript. I tried to make a carousel using CSS and JavaScript.
I would like to know how do I create the logic for the dots on my custom carousel?
I created the buttons, and they are working to pass the slides. But can you tell me how do I create the dots?
This is my project into codesandbox
export function usePosition(ref) {
const [prevElement, setPrevElement] = React.useState(null);
const [nextElement, setNextElement] = React.useState(null);
React.useEffect(() => {
const element = ref.current;
const update = () => {
const rect = element.getBoundingClientRect();
const visibleElements = Array.from(element.children).filter((child) => {
const childRect = child.getBoundingClientRect();
return rect.left <= childRect.left && rect.right >= childRect.right;
});
if (visibleElements.length > 0) {
setPrevElement(getPrevElement(visibleElements));
setNextElement(getNextElement(visibleElements));
}
};
update();
element.addEventListener("scroll", update, { passive: true });
return () => {
element.removeEventListener("scroll", update, { passive: true });
};
}, [ref]);
const scrollToElement = React.useCallback(
(element) => {
const currentNode = ref.current;
if (!currentNode || !element) return;
let newScrollPosition;
newScrollPosition =
element.offsetLeft +
element.getBoundingClientRect().width / 2 -
currentNode.getBoundingClientRect().width / 2;
console.log("newScrollPosition: ", newScrollPosition);
currentNode.scroll({
left: newScrollPosition,
behavior: "smooth"
});
},
[ref]
);
const scrollRight = React.useCallback(() => scrollToElement(nextElement), [
scrollToElement,
nextElement
]);
return {
hasItemsOnLeft: prevElement !== null,
hasItemsOnRight: nextElement !== null,
scrollRight,
scrollLeft
};
}
Thank you in advance for any help!!
below you will find my solution, I took your code from the sandbox and worked with it. I didn't understand if you wanted to show the dots, to scroll and sync the dots and click on the dots to change the image, so I did all of them .
codesandbox
Related
In my form, I want to use keyboard arrow keys to move between the input fields.
Here is the Codesandbox example.
(Click inside the last input field and use the UP arrow key to move up to the previous sibling input field.
Code:
const arrowDownPosition = React.useRef();
const getCurElePosition = (eTarget) => {
const mum = eTarget.parentElement.parentElement;
const parent = mum.parentElement;
const children = Array.from(parent.children);
let taskNum;
children.forEach((el, i) => {
const elChild = el.children[0].children[0];
if (elChild === eTarget) {
taskNum = i;
}
});
return [taskNum, parent, children, mum];
};
const keyUpHandler = (e) => {
e.preventDefault();
if (e.key === "ArrowUp") {
e.target.setSelectionRange(
arrowDownPosition.current[0],
arrowDownPosition.current[1]
);
}
};
const keyHandler = (e) => {
e.stopPropagation();
const eTarget = e.target;
const [elePosition, parent, children, mum] = getCurElePosition(eTarget);
if (e.key === "ArrowUp") {
let previousSiblingRef;
children.forEach((ele, i) => {
if (ele == mum && i - 1 >= 0) {
previousSiblingRef = i - 1;
}
});
if (previousSiblingRef) {
const { selectionStart, selectionEnd } = eTarget;
arrowDownPosition.current = [selectionStart, selectionEnd];
const previousSibling =
children[previousSiblingRef].children[0].children[0];
previousSibling.focus();
previousSibling.setSelectionRange(selectionStart, selectionEnd);
}
}
};
The above solution is a hacky way of solving the problem. You can see the caret move to the start of the previous input field before its moved to my intended position. I am using useRef to store the value.
This is because I am unable to stop the keyUp default handler before my code is run?
Thank you
This question already has answers here:
Rerender view on browser resize with React
(25 answers)
Closed 1 year ago.
I use useState to get the window height but it gives a runtime error saying that window is not defined. Do you know why
Here's the code:
let [winHeight,setWinHeight] = useState(window.innerHeight)
useEffect(() => {
const list = []
for (var i=0;i<datas.length;i++){
const t = datas[i].title
const res = handleResize(i + 2)
list.push(<li ref={resl[i + 1]} style={{top: "20px",left: res + "px"}}><Anchor href={datas[i].link || `/${t.replace(/ /g, "_")}`}><a onClick={() => closeNav()} onMouseOver={() => setHovering(i)}>{t}</a></Anchor></li>)
}
setWinHeight(window.innerHeight)
setLinks(list)
}, [winHeight])
Have you tried adding window.height in useEffect's dependency array?
useEffect(() => {
const list = []
for (var i=0;i<datas.length;i++){
const t = datas[i].title
const res = handleResize(i + 2)
list.push(<li ref={resl[i + 1]} style={{top: "20px",left: res + "px"}}><Anchor href={datas[i].link || `/${t.replace(/ /g, "_")}`}><a onClick={() => closeNav()} onMouseOver={() => setHovering(i)}>{t}</a></Anchor></li>)
}
setLinks(list)
}, [window.innerHeight])
Try this:
let [winHeight,setWinHeight] = useState(0)
useEffect(()=> {
setWinHeight(window.innerHeight)
},[])
And also you need to import useEffect from 'react'.
window isn't defined because the component isn't renderer at the point you are trying to access window object.
use [window.innerHeight] not [winHeight] inside dependency Array of UseEffect
Are you running on RN? Then you can try this to get the height~
import { Dimensions } from 'react-native';
const windowHeight = Dimensions.get('window').height;
Last Post
Add the resize event listener
Following this one, it would update the winHeight when you change height of window
const lastHeight = useRef(window.innerHeight);
const [winHeight, setWinHeight] = useState(lastHeight.current);
useEffect(() => {
window.addEventListener('resize', (e) => {
const curHeight = e.currentTarget?.innerHeight;
if (lastHeight.current !== curHeight) {
lastHeight.current = curHeight;
setWinHeight(curHeight);
}
});
}, []);
I can't understand the calculations in this code .
It's a video on youtube and this is the link for it : https://www.youtube.com/watch?v=jfYWwQrtzzY
I can't understand how he calculated the target element and how to put the dragged element in certain position.
const draggables = document.querySelectorAll(".draggable");
const containers = document.querySelectorAll(".container");
draggables.forEach((draggable) => {
draggable.addEventListener("dragstart", () => {
draggable.classList.add("dragging");
});
draggable.addEventListener("dragend", () => {
draggable.classList.remove("dragging");
});
});
containers.forEach((container) => {
container.addEventListener("dragover", (e) => {
e.preventDefault();
const afterElement = getDragAfterElement(container, e.clientY);
const draggable = document.querySelector(".dragging");
if (afterElement == null) {
container.appendChild(draggable);
}else{
container.insertBefore(draggable,afterElement);
}
});
});
function getDragAfterElement(container, y) {
const draggableElements = [
...container.querySelectorAll(".draggable:not(.dragging)"),
];
return draggableElements.reduce(
(closest, child) => {
const box = child.getBoundingClientRect();
const offset = y - box.top - box.height / 2;
if (offset < 0 && offset > closest.offset) {
return { offset: offset, element: child };
} else {
return closest;
}
},
{ offset: Number.NEGATIVE_INFINITY }
).element;
}
I'm experimenting with drag-and-drop using cyclejs in a codepen. The standard drag methods supported by HTML 5 don't seem to support constraints on the movement of the dragged object so I went with standard mousedown/mousemove/mouseup. It works, but not consistently. The combine() operation doesn't seem to trigger even when the debug() calls show that mousedown and mousemove events have been received and sometimes the mouseup is missed. Perhaps my understanding of the operation is incomplete or incorrect. A direct link to the codepen is provided at the bottom of this post. Any help appreciated!
const xs = xstream.default;
const { run } = Cycle;
const { div, svg, makeDOMDriver } = CycleDOM;
function DragBox(sources) {
const COMPONENT_NAME = `DragBox`;
const intent = function({ DOM }) {
return {
mousedown$: DOM.select(`#${COMPONENT_NAME}`)
.events("mousedown")
.map(function(ev) {
return ev;
})
.debug("mousedown"),
mousemove$: DOM.select(`#${COMPONENT_NAME}`)
.events("mousemove")
.map(function(ev) {
return ev;
})
.debug("mousemove"),
mouseup$: DOM.select("#container")
.events("mouseup")
.map(function(ev) {
return ev;
})
.debug("mouseup")
};
};
const between = (first, second) => {
return source => first.mapTo(source.endWhen(second)).flatten();
};
const model = function({ mousedown$, mousemove$, mouseup$ }) {
return xs
.combine(mousedown$, mousemove$)
.debug("combine")
.map(([mousedown, mousemove]) => ({
x: mousemove.pageX - mousedown.layerX,
y: mousemove.pageY - mousedown.layerY
}))
.compose(between(mousedown$, mouseup$))
.startWith({
x: 0,
y: 0
})
.debug("model");
};
const getStyle = left => top => {
return {
style: {
position: "absolute",
left: left + "px",
top: top + "px",
backgroundColor: "#333",
cursor: "move"
}
};
};
const view = function(state$) {
return state$.map(value =>
div("#container", { style: { height: "100vh" } }, [
div(`#${COMPONENT_NAME}`, getStyle(value.x)(value.y), "Move Me!")
])
);
};
const actions = intent(sources);
const state$ = model(actions);
const vTree$ = view(state$);
return {
DOM: vTree$
};
}
function main(sources) {
const dragBox = DragBox(sources);
const sinks = {
DOM: dragBox.DOM
};
return sinks;
}
Cycle.run(main, {
DOM: makeDOMDriver("#app")
});
https://codepen.io/velociflapter/pen/bvqMGp?editors=1111
Testing your code shows that combine is not getting the first mousedown event, apparently due to the between operator subscribing to mousedown$ after the first mousedown event. Adding remember to the mousedown$ sends that first mousedown event to the between operator subscription.
mousedown$: DOM.select(`#${COMPONENT_NAME}`)
.events("mousedown").remember()
Codepen.io remember example.
Codesandbox.io testing between
Here's another CycleJS/xstream Drag and Drop approach (taking inspiration from this RxJS Drag and Drop example) I think is more straight forward. Everything else in your code is essentially the same but the model function is this:
const model = function({ mousedown$, mousemove$, mouseup$ }) {
return mousedown$.map(md => {
let startX = md.offsetX, startY = md.offsetY
return mousemove$.map(mm => {
mm.preventDefault()
return {
x: mm.clientX - startX,
y: mm.clientY - startY
}
}).endWhen(mouseup$)
}).flatten().startWith({x:0,y:0})
};
Here's a Codepen.io example.
How to limit max characters in draft js?
I can get length of the state like that, but how to stop updating component?
var length = editorState.getCurrentContent().getPlainText('').length;
You should define handleBeforeInput and handlePastedText props. In handler-functions, you check the length of current content + length of pasted text and if it reaches the maximum you should return 'handled' string.
UPD 21.03.2018: Upgraded to the last versions of react/react-dom (16.2.0) and Draft.js (0.10.5).
Working example - https://jsfiddle.net/Ln1hads9/11/
const {Editor, EditorState} = Draft;
const MAX_LENGTH = 10;
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {
editorState: EditorState.createEmpty()
};
}
render() {
return (
<div className="container-root">
<Editor
placeholder="Type away :)"
editorState={this.state.editorState}
handleBeforeInput={this._handleBeforeInput}
handlePastedText={this._handlePastedText}
onChange={this._handleChange}
/>
</div>
);
}
_getLengthOfSelectedText = () => {
const currentSelection = this.state.editorState.getSelection();
const isCollapsed = currentSelection.isCollapsed();
let length = 0;
if (!isCollapsed) {
const currentContent = this.state.editorState.getCurrentContent();
const startKey = currentSelection.getStartKey();
const endKey = currentSelection.getEndKey();
const startBlock = currentContent.getBlockForKey(startKey);
const isStartAndEndBlockAreTheSame = startKey === endKey;
const startBlockTextLength = startBlock.getLength();
const startSelectedTextLength = startBlockTextLength - currentSelection.getStartOffset();
const endSelectedTextLength = currentSelection.getEndOffset();
const keyAfterEnd = currentContent.getKeyAfter(endKey);
console.log(currentSelection)
if (isStartAndEndBlockAreTheSame) {
length += currentSelection.getEndOffset() - currentSelection.getStartOffset();
} else {
let currentKey = startKey;
while (currentKey && currentKey !== keyAfterEnd) {
if (currentKey === startKey) {
length += startSelectedTextLength + 1;
} else if (currentKey === endKey) {
length += endSelectedTextLength;
} else {
length += currentContent.getBlockForKey(currentKey).getLength() + 1;
}
currentKey = currentContent.getKeyAfter(currentKey);
};
}
}
return length;
}
_handleBeforeInput = () => {
const currentContent = this.state.editorState.getCurrentContent();
const currentContentLength = currentContent.getPlainText('').length;
const selectedTextLength = this._getLengthOfSelectedText();
if (currentContentLength - selectedTextLength > MAX_LENGTH - 1) {
console.log('you can type max ten characters');
return 'handled';
}
}
_handlePastedText = (pastedText) => {
const currentContent = this.state.editorState.getCurrentContent();
const currentContentLength = currentContent.getPlainText('').length;
const selectedTextLength = this._getLengthOfSelectedText();
if (currentContentLength + pastedText.length - selectedTextLength > MAX_LENGTH) {
console.log('you can type max ten characters');
return 'handled';
}
}
_handleChange = (editorState) => {
this.setState({ editorState });
}
}
ReactDOM.render(<Container />, document.getElementById('react-root'))
Mikhail's methods are correct, but the handler return value is not. 'not_handled' is a fall-through case that allows the Editor component to process the input normally. In this case, we want to stop the Editor from processing input.
In older versions of DraftJS, it looks like the presence of a string evaluated to 'true' in the handling code, and so the above code behaved correctly. In later versions of DraftJS, the above fiddle doesn't work - I don't have the reputation to post more that one Fiddle here, but try Mikhail's code with v0.10 of DraftJS to replicate.
To correct this, return 'handled' or true when you don't want the Editor to continue handling the input.
Fiddle with corrected return values
For example,
_handleBeforeInput = () => {
const currentContent = this.state.editorState.getCurrentContent();
const currentContentLength = currentContent.getPlainText('').length
if (currentContentLength > MAX_LENGTH - 1) {
console.log('you can type max ten characters');
return 'handled';
}
}
See the DraftJS docs on Cancelable Handlers for more.
This is a bit of an old thread, but thought I would share a solution for anyone else facing the problem of character limit and behaviour while pasting text...
Put together something pretty quickly based on the above code by Mikhail to handle this use case which works for me - although I haven't done any work on trying to optimise it.
Basically the handle pasted text looks like so:
const __handlePastedText = (pastedText: any) => {
const currentContent = editorState.getCurrentContent();
const currentContentLength = currentContent.getPlainText('').length;
const selectedTextLength = _getLengthOfSelectedText();
if (currentContentLength + pastedText.length - selectedTextLength > MAX_LENGTH) {
const selection = editorState.getSelection()
const isCollapsed = selection.isCollapsed()
const tempEditorState = !isCollapsed ? _removeSelection() : editorState
_addPastedContent(pastedText, tempEditorState)
return 'handled';
}
return 'not-handled'
}
We have a helper function to handle the deletion of the selection prior to pasting new characters which returns the new editor state:
const _removeSelection = () => {
const selection = editorState.getSelection()
const startKey = selection.getStartKey()
const startOffset = selection.getStartOffset()
const endKey = selection.getEndKey()
const endOffset = selection.getEndOffset()
if (startKey !== endKey || startOffset !== endOffset) {
const newContent = Modifier.removeRange(editorState.getCurrentContent(), selection, 'forward')
const tempEditorState = EditorState.push(
editorState,
newContent,
"remove-range"
)
setEditorState(
tempEditorState
)
return tempEditorState
}
return editorState
}
and finally the function to add the pasted text with a limit:
const _addPastedContent = (input: any, editorState: EditorState) => {
const inputLength = editorState
.getCurrentContent()
.getPlainText().length;
let remainingLength = MAX_LENGTH - inputLength;
const newContent = Modifier.insertText(
editorState.getCurrentContent(),
editorState.getSelection(),
input.slice(0,remainingLength)
);
setEditorState(
EditorState.push(
editorState,
newContent,
"insert-characters"
)
)
}
Link to worked example:
https://codesandbox.io/s/objective-bush-1h9x6
As Mikhail mentioned, you need to handle typing and pasting text. Here are both handlers. Note the paste handler will preserve text that is not outside the limit
function handleBeforeInput(text: string, state: EditorState): DraftHandleValue {
const totalLength = state.getCurrentContent().getPlainText().length + text.length;
return totalLength > MAX_LENGTH ? 'handled' : 'not-handled';
}
function handlePastedText(text: string, _: string, state: EditorState): DraftHandleValue {
const overflowChars = text.length + state.getCurrentContent().getPlainText().length - MAX_LENGTH;
if (overflowChars > 0) {
if (text.length - overflowChars > 0) {
const newContent = Modifier.insertText(
state.getCurrentContent(),
state.getSelection(),
text.substring(0, text.length - overflowChars)
);
setEditorState(EditorState.push(state, newContent, 'insert-characters'));
}
return 'handled';
} else {
return 'not-handled';
}
}
Let's think about this for a second. What is called to make the changes? Your onChange, right? Good. We also know the length. Correct? We attact the "worker" which is the onChange:
const length = editorState.getCurrentContent().getPlainText('').length;
// Your onChange function:
onChange(editorState) {
const MAX_LENGTH = 10;
const length = editorState.getCurrentContent().getPlainText('').length;
if (length <= MAX_LENGTH) {
this.setState({ editorState }) // or this.setState({ editorState: editorState })
}
} else {
console.log(`Sorry, you've exceeded your limit of ${MAX_LENGTH}`)
}
I have not tried this but my 6th sense says it works just fine.