I have a DIV which has contenteditable attribute and I am running 70 lines of JavaScript code on input event but when I try to type fast. The JavaScript code is very slow and the results don't show/update in the UI as quickly as expected.
What I am doing in the JavaScript is in the input event:
I am getting the textcontent of the DIV with contenteditable attribute and then changing it to array with split() function and then, by using a for loop, I am comparing the array values with other array. If the value matches I create a SPAN with textContent set as the current value from that contenteditable DIV and then put it in an spanArray array. Then I'm appending all those SPANs into the contenteditable DIV and setting the caret at the end.
How to optimize the code so that the type speed / performance is not affected by the JavaScript's heavy for loops seen in the below example?
Here is the code I got so far:
const mainDiv = document.querySelector("#mainDiv");
const placeHolderDiv = document.querySelector("#placeholder");
let placeHolder = "";
let placeHolderArr;
fetch("https://type.fit/api/quotes")
.then((data) => {
return data.json();
})
.then((result) => {
for (let quote = 0; quote < 10; quote++) {
placeHolder += result[quote].text;
placeHolderArr = placeHolder.split(" ");
placeHolderDiv.textContent = placeHolder;
}
});
mainDiv.addEventListener("input", (e) => {
let spanArr = [];
const mainDivArr = mainDiv.textContent.split(" ");
let num = 0;
if (e.code !== "Space") {
for (let i of mainDivArr) {
if (placeHolderArr[num].trim() === mainDivArr[num].trim()) {
const span = document.createElement("span");
span.textContent = mainDivArr[num] + " ";
mainDiv.innerHTML = "";
spanArr.push(span);
num++;
} else if (placeHolderArr[num].trim() !== mainDivArr[num].trim()) {
const span = document.createElement("span");
span.style.color = "red";
span.textContent = mainDivArr[num] + " ";
mainDiv.innerHTML = "";
spanArr.push(span);
num++;
}
for (let spans of spanArr) {
mainDiv.innerHTML += spans.outerHTML;
// Placing Caret At The End
function placeCaretAtEnd(el) {
el.focus();
if (
typeof window.getSelection != "undefined" &&
typeof document.createRange != "undefined"
) {
var range = document.createRange();
range.selectNodeContents(el);
range.collapse(false);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (typeof document.body.createTextRange != "undefined") {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(false);
textRange.select();
}
}
placeCaretAtEnd(mainDiv);
}
}
} else {
console.log("space pressed");
}
});
body {
height: 90vh;
display: flex;
justify-content: center;
align-items: center;
font-family: sans-serif;
font-size: 1.5rem;
}
#content {
position: relative;
color: grey;
width: 70vw;
}
#mainDiv {
position: absolute;
top: 0;
left: 0;
color: #000;
width: 100%;
border: none;
outline: none;
}
<div id="content">
<div id="mainDiv" autofocus contenteditable></div>
<div id="placeholder"></div>
</div>
Consider another approach to the problem.
Instead of doing heavy operations over a contenteditable, disrupting such an area while the user is inserting text / typing, using two nested heavy for loops (O(n^2) complexity), and defining functions within a for loop (all really bad practices), I would suggest:
use only one DIV element
populate it with SPANs for words and characters
set it as tabindex="1" so that you can focus it and capture keyboard events
create an index variable idx so you can keep track on the progress and add classes to every character SPAN
on "keydown" event, use Event.key to determine the pressed key character. If the character matches with the current expected character, advance the index
const text = `Focus and start typing. Genius is one percent inspiration and ninety-nine percent perspiration.`;
const elText = document.querySelector("#text");
const textChars = [];
const words = text.split(/(?<=\S+\s)/g);
// Create SPAN for words and characters
elText.innerHTML = words.reduce((html, word) => {
textChars.push(...word);
const chars = [...word].map(ch => `<span class="char">${ch}</span>`).join("");
return html + `<span class="word">${chars}</span>`;
}, "");
const elsChars = elText.querySelectorAll(".char");
const totChars = textChars.length;
let totIncorrect = 0;
let isGameOver = false;
let idx = 0; // type progress index
elsChars[idx].classList.add("current");
elText.addEventListener("keydown", (ev) => {
if (isGameOver) return; // Prevent any more typing
ev.preventDefault(); // Prevent spacebar scrolling etc...
let typeChar = ev.key;
if (typeChar === "Enter") typeChar = " "; // Treat Enter as Space
if (typeChar.length > 1) return; // Do nothing on special keys
// CORRECT:
if (typeChar === textChars[idx]) {
elsChars[idx].classList.add("ok");
}
// INCORRECT:
else {
elsChars[idx].classList.add("err");
totIncorrect += 1;
}
// ADVANCE:
elsChars[idx].classList.remove("current");
idx += 1;
elsChars[idx]?.classList.add("current");
// GAME OVER:
if (idx === totChars) {
isGameOver = true;
console.log(`Well done! You had ${totIncorrect} mistakes out of ${totChars}!`);
}
});
body {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font: 1.5rem/1.3 sans-serif;
}
#text {
position: relative;
padding: 1rem;
color: grey;
width: 60vw;
color: #888;
letter-spacing: 0.15rem;
}
#text:focus {
outline: 1px solid #ddd;
}
.char {
display: inline;
white-space: pre-wrap;
}
#text:focus .current {
animation: blink 1s infinite;
}
#keyframes blink {
0% {box-shadow: -3px 0 0 #000;}
50% {box-shadow: -3px 0 0 #000;}
100% {box-shadow: -3px 0 0 transparent;}
}
.ok {
color: #000;
background: hsla(200 , 100%, 50%, 0.1);
}
.err {
color: #f00;
background: hsla(0, 100%, 50%, 0.1);
}
<div id="text" tabindex="1"></div>
With this simpler solution, now you can type as fast as it goes :)
For mobile devices you could incorporate an overlay transparent textarea in order to grab focus and prompt the onscreen keyboard.
i am trying to add tooltip and data over hover of a sprite inside using the data visualization api. I have achieved adding the sprite viewable, but it is not mentioned as to how we add a tooltip on hover. Docs just mention there is a DATAVID_OBJECT_HOVERING event that is triggered to the viewer object, and we can add a callback on it. Also the code is not clear. Attaching hyperion demo's code below which only adds a callback to the onhover event on sprite and no mention of tooltip and data passed to it. Where in the code that is present as in this website i am seeing the tooltip with some data. this is the link https://hyperion.autodesk.io/ and code is this https://github.dev/Autodesk-Forge/forge-dataviz-iot-reference-app
async function onModelLoaded(viewer) {
const dataVizExt = viewer.getExtension("Autodesk.DataVisualization");
const DATAVIZEXTN = Autodesk.DataVisualization.Core;
var styleMap = {};
// Create model-to-style map from style definitions.
Object.entries(SensorStyleDefinitions).forEach(([type, styleDef]) => {
styleMap[type] = new DATAVIZEXTN.ViewableStyle(
DATAVIZEXTN.ViewableType.SPRITE,
new THREE.Color(styleDef.color),
styleDef.url
);
});
const viewableData = new DATAVIZEXTN.ViewableData();
viewableData.spriteSize = 16;
let startId = 1;
devices.forEach((device) => {
let style = styleMap[device.type] || styleMap["default"];
const viewable = new DATAVIZEXTN.SpriteViewable(device.position, style, startId);
viewableData.addViewable(viewable);
startId++;
});
await viewableData.finish();
dataVizExt.addViewables(viewableData);
/**
* Called when a user clicks on a Sprite Viewable
* #param {Event} event
*/
function onItemClick(/* event */) {}
/**
* Called when a user hovers over a Sprite Viewable
* #param {Event} event
*/
function onItemHovering(event) {
console.log("Show tooltip here", event.dbId);
}
const DataVizCore = Autodesk.DataVisualization.Core;
viewer.addEventListener(DataVizCore.MOUSE_CLICK, onItemClick);
viewer.addEventListener(DataVizCore.MOUSE_HOVERING, onItemHovering);
}
May check out my BIM360SensorTooltip at forge-bim360-assets.viewer/BIM360IotConnectedExtension.js#L300. The core concept is like the following
Create your custom tooltip by JavaScript DOM API.
To add a tooltip for a sprite, just add some codes in your onItemHovering event to create/show your custom tooltip.
Here is an example of my BIM360SensorTooltip (removed BIM360 prefix):
class SensorTooltip extends THREE.EventDispatcher {
constructor(parent) {
super();
this.parent = parent;
this.init();
}
get viewer() {
return this.parent.viewer;
}
get dataVizTool() {
return this.parent.dataVizTool;
}
init() {
const container = document.createElement('div');
container.classList.add('bim360-sensor-tooltip');
this.container = container;
const bodyContainer = document.createElement('div');
bodyContainer.classList.add('bim360-sensor-tooltip-body');
container.appendChild(bodyContainer);
this.bodyContainer = bodyContainer;
bodyContainer.innerHTML = 'No Data';
this.viewer.container.appendChild(container);
}
setVisible(visible) {
if (visible) {
this.bodyContainer.classList.add('visible');
} else {
this.bodyContainer.classList.remove('visible');
}
}
setPosition(point) {
const contentRect = this.bodyContainer.getBoundingClientRect();
const offsetX = contentRect.width / 2;
const spriteSize = this.dataVizTool.viewableData.spriteSize;
const offsetY = contentRect.height + 0.7 * spriteSize / this.parent.viewer.getWindow().devicePixelRatio;
const pos = new THREE.Vector3(
point.x - offsetX,
point.y - offsetY,
0
);
this.container.style.transform = `translate3d(${pos.x}px, ${pos.y}px, ${pos.z}px)`;
}
setPositionByWordPoint(point) {
this.setPosition(this.viewer.worldToClient(point));
}
async show(sensor) {
if (!sensor) return;
this.bodyContainer.innerHTML = '';
const nameSpan = document.createElement('span');
nameSpan.classList.add('bim360-sensor-tooltip-name');
this.bodyContainer.appendChild(nameSpan);
const assetInfo = this.parent.dataProvider.assetInfoCache[sensor.externalId];
let nameString = 'Unknown asset';
if (assetInfo) {
nameString = `Asset [${assetInfo.assetId}]`;
}
nameSpan.innerHTML = `${nameString} ${sensor.name}`;
const valueSpan = document.createElement('span');
valueSpan.classList.add('bim360-sensor-tooltip-value');
this.bodyContainer.appendChild(valueSpan);
let cachedData = this.parent.dataHelper.getDataFromCache(sensor.id, sensor.name);
if (cachedData) {
let value = Utility.getClosestValue(cachedData, Utility.getTimeInEpochSeconds(this.parent.currentTime));
let valueString = `${value.toFixed(2)}`;
if (sensor.dataUnit)
valueString += ` ${sensor.dataUnit}`;
valueSpan.innerHTML = valueString;
}
this.setVisible(true);
this.setPosition(this.viewer.worldToClient(sensor.position));
}
hide() {
this.bodyContainer.innerHTML = 'No Data';
this.setVisible(false);
}
}
const tooltip = new SensorTooltip(yourExtesionLoadedTheDataVisulationExtesion);
const onSensorHovered => (event) {
if (event.hovering && dbId2DeviceIdMap) {
const deviceId = dbId2DeviceIdMap[event.dbId];
const { sensors } = dataProvider;
if (!sensors || sensors.length <= 0) return;
const sensor = sensors.find(s => s.externalId == deviceId);
if (!sensor) return;
tooltip.show(sensor);
} else {
tooltip.hide();
}
};
viewer.addEventListener(
Autodesk.DataVisualization.Core.MOUSE_HOVERING,
onSensorHovered
);
and its CSS style see https://github.com/yiskang/forge-bim360-assets.viewer/blob/bim360-iot-connected/bim360assets/wwwroot/css/main.css#L182
/** DataViz Sensor Tooltip **/
/*https://www.w3schools.com/howto/tryit.asp?filename=tryhow_css_tooltip*/
.bim360-sensor-tooltip {
visibility: hidden;
position: absolute;
display: inline-block;
border-bottom: 1px dotted black;
top: 0;
left: 0;
width: 170px;
}
.bim360-sensor-tooltip .bim360-sensor-tooltip-body {
width: 170px;
background-color: #555;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 0;
position: absolute;
z-index: 1;
left: 50%;
margin-left: -85px;
opacity: 0;
transition: opacity 0.3s;
visibility: hidden;
width: 170px;
background-color: #555;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 0;
position: absolute;
z-index: 1;
opacity: 0;
transition: opacity 0.3s;
font-size: 12px;
}
.bim360-sensor-tooltip .bim360-sensor-tooltip-body::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: #555 transparent transparent transparent;
}
.bim360-sensor-tooltip .bim360-sensor-tooltip-body.visible {
visibility: visible;
opacity: 1;
}
.bim360-sensor-tooltip-name {
display: flex;
justify-content: center;
font-weight: bold;
font-size: 12px;
padding-top: 1px;
padding-bottom: 3px;
}
.bim360-sensor-tooltip-value {
font-size: 13px;
display: flex;
justify-content: center;
}
I want to show the very hard array and hard array in the textarea. Right now, it shows under the textarea as I don't know how to show it in the textarea. The user gives the input and the server response with the hard and very hard sentences from the user input. The hard sentences have a yellow background and the very hard have red background. For now, only the hard and very hard sentences with yellow and red background respectively is shown below the textarea and not the whole thing but I think it isn't intuitive as the user would have to go and search for the sentences in the textarea again as where the sentence exactly lies. So I want the whole thing to be shown in the textarea itself with the hard and very hard sentences highlighted in yellow and red background.
Right now my code looks something like this:
state={
hardArray: [],
vhardArray: []
}
performHard = async () => {
const { enteredText } = this.state;
const body = { text: enteredText };
const stringifiedBody = JSON.stringify(body);
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: stringifiedBody
};
const url = "api/same";
try {
const response = await fetch(url, options);
const result = await response.json();
this.setState(prevState => ({
hardArray: [...prevState.hardArray, ...result.hard]
}));
} catch (error) {
console.error("error");
}
};
performVHard = async () => {
const { enteredText } = this.state;
const body = { text: enteredText };
const stringifiedBody = JSON.stringify(body);
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: stringifiedBody
};
const url ="api/same";
try {
const response = await fetch(url, options);
const result = await response.json();
this.setState(prevState => ({
vhardArray: [...prevState.vhardArray, ...result.very_hard]
}));
} catch (error) {
console.error("error");
}
};
performAnalysis = () => {
this.performHard();
this.performVHard();
};
<textarea
name="enteredText"
className="textareaclass"
placeholder="Enter your text here"
onChange={this.handleChange}
value={enteredText}
></textarea>
<Button
className="rectangle-3"
onClick={this.performAnalysis}>Analyse
</Button>
<div>
{this.state.hardArray.map((word, i) => (
<span className="hardColor">{word}</span>
))}
{this.state.vhardArray.map((word, i) => (
<span className="vhardColor">{word}</span>
))}
</div>
edit: this is how I receive the respond from the server
{
"hard": [
"It's the possibility of having a dream come true that makes life interesting",
"I cannot fix on the hour, or the spot, or the look or the words, which laid the foundation.",
]
"very_hard": [
“He stepped down, trying not to look long at her, as if she were the sun, yet he saw her, like the sun, even
without looking.”
]
}
I want to show all the text in the same textarea where the user wrote his content instead of showing anywhere else in the browser as it will make everything look ugly.
You can only give a textarea one background color, but you can make it transparent and put things behind it to add some color, yea this is a total hack and you will need to fiddle with the sizes and the blank lines to move the text down a bit - I will leave that exercise to you.
This also does not show how to get your values into the textarea but that is simple JavaScript/react code perhaps.
I altered this with some functions to illustrate where you MIGHT simply add/remove blank lines in the textarea to match the height of the background color - would probably have to adjust that background to match when this overflows the size, OR you might adjust the background to make it smaller for the colors.
I will leave it to you to determine which is the better option, I used "|" and "||" as the line/section separators as once it is in the textarea and edited you will need something like that.
All I have time for right now to enhance this but should give a starting point for this somewhat edge case without a clear standard solution.
.message {
width: 300px;
height: 150px;
display: block;
position: absolute;
}
textarea.format-custom,
.custom-underlay,
.underlay {
margin: 0;
box-sizing: border-box;
vertical-align: top;
display: block;
position: absolute;
border: lime solid 1px;
}
textarea.format-custom {
width: 100%;
height: 100%;
background: transparent;
resize: none;
display: block;
}
.underlay {
width: 100%;
height: 100%;
background: transparent;
display: block;
z-index: -1;
}
.custom-underlay {
width: 100%;
height: 50%;
margin: 0;
box-sizing: border-box;
vertical-align: top;
}
.custom-underlay.top {
background-color: #FFDDDD;
top: 0;
left: 0;
}
.custom-underlay.bottom {
background-color: #DDDDFF;
top: 50%;
left: 0;
}
<div class="message">
<label for="msg">Your message:</label>
<textarea id="msg" name="user_message" class="format-custom">howdy, I am here
bottom of here</textarea>
<div class="underlay">
<div class="custom-underlay top"></div>
<div class="custom-underlay bottom"></div>
</div>
</div>
Alternate idea from question, put text on the div's behind:
'use strict';
// borrowed code from https://stackoverflow.com/a/17590149/125981
// makeClass - By Hubert Kauker (MIT Licensed)
// original by John Resig (MIT Licensed).
var makeClass = (function(Void) {
return function() {
var constructor = function() {
var init = constructor.prototype.init,
hasInitMethod = (typeof init == "function"),
instance;
if (this instanceof constructor) {
if (hasInitMethod) init.apply(this, arguments);
} else {
Void.prototype = constructor.prototype;
instance = new Void();
if (hasInitMethod) init.apply(instance, arguments);
return instance;
}
};
return constructor;
};
})(function() {});
//make a class MyApp using above
var MyApp = makeClass();
// create MyApp functional part using the init:
MyApp.prototype.init = function(myItem, showmeClass = "showme", separator = "|", groupSeparator = "||") {
let eventChangeName = "change";
let textElement = document.getElementById(myItem);
let showme = textElement.closest(".container").getElementsByClassName(showmeClass)[0];
let lineSep = "|\n";
let myData = {
hard: [],
very_hard: []
};
this.sentData = {
hard: [],
very_hard: []
};
//so we can tell the lines
function getStyle(elId, styleProp) {
var x = document.getElementById(elId);
let y = {};
if (x.currentStyle) {
y = x.currentStyle[styleProp];
} else if (window.getComputedStyle) {
y = document.defaultView.getComputedStyle(x, null).getPropertyValue(styleProp);
}
return y;
}
function getTextareaThings(myTextarea) {
let taComputedStyles = window.getComputedStyle(myTextarea);
return {
height: myTextarea.style.height,
rows: myTextarea.rows,
clientHeight: myTextarea.clientHeight,
lineHeight: taComputedStyles.getPropertyValue('line-height'),
font: taComputedStyles.getPropertyValue('font-size')
};
}
function getLinesInString(myString) {
/* new line things: /r old Mac, /cr/lf some, /n some
all the "new line": regex: /\r\n|\n|\r/gm
above reduced regex: g and m are for global and multiline flags */
let nl = /[\r\n]+/gm;
let lines = [];
lines = myString.split(nl);
return lines;
}
function splitGroupString(myString, separator) {
let strings = [];
strings = myString.split(separator);
return strings;
}
function getGroupsInString(myString) {
return splitGroupString(myString, groupSeparator);
}
function getGroupItemsInString(myString) {
return splitGroupString(myString, separator);
}
function getCurrentValue() {
return textElement.value;
}
function addNewLines(text, count) {
let newLine = "\n";
return text + newLine.repeat(count);
}
// make stuff obvious
function onFocusTextareaValue(event) {
showForDebug(event);
}
function onChangeTextareaValue(event) {
if (event.type == eventChangeName) {
event.stopPropagation();
event.stopImmediatePropagation();
}
showForDebug(event);
}
function showForDebug(event) {
let what = "Event: " + event.type;
let b = "<br />";
let tat = getTextareaThings(event.target);
let v = getCurrentValue().replace(what, "");
showme.innerHTML = what + b + ": lines:" + getLinesInString(v).length + b + v;
}
function getStringLineCount(arr) {
arr.length;
}
function getGroupItems() {
let groups = getGroupsInString(getCurrentValue());
let groupItems = {
count: groups.length, // how many groups, two in the definition (top/bottom)
groups: []
};
groups.forEach(function(group, index, groupsArr) {
let items = getGroupItemsInString(group);
// determine how to define "group name", I use a string and the index here
let gname = "group" + index;
let g = {
itemCount: items.length // number in this group
};
g[gname] = {
items: []
};
items.forEach(function(item, itemindex, itemsArr) {
let itemName = "item" + itemindex;
let itemobj = {};
itemobj[itemName] = {
items: item
};
g[gname].items.push(itemobj);
});
groupItems.groups.push(g);
});
return groupItems;
}
// setup events
textElement.addEventListener(eventChangeName, onChangeTextareaValue, false);
textElement.addEventListener("focus", onFocusTextareaValue, false);
this.getGeometry = function() {
let geometry = {};
let element = textElement;
let rect = element.getBoundingClientRect();
geometry.top = rect.top;
geometry.right = rect.right;
geometry.bottom = rect.bottom;
geometry.left = rect.left;
geometry.offsetHeight = element.offsetHeight;
geometry.rows = element.rows;
geometry.clientHeight = element.clientHeight;
geometry.fontSize = this.getStyleProperty("font-size");
geometry.lineCount = this.getLines().length;
geometry.lineHeight = this.getLineHeight();
geometry.height = geometry.bottom - geometry.top;
geometry.width = geometry.right - geometry.left;
console.log("Geometry:",geometry);
};
this.getMetrics = function() {
let fSize = this.getStyleProperty("font-size");
let lineCount = this.getLines().length;
let lineHeight = this.getLineHeight();
let yh = lineHeight / lineCount;
let yfhPixel = parseInt(fSize, 10);
let yLineY = yh * yfhPixel;
console.log("LH:", lineHeight, "font:", fSize, "Lines:", lineCount, "lineHeight:", lineHeight, "yh:", yh, "yfPixel:", yfhPixel, "yLineY:", yLineY);
};
this.getStyleProperty = function(propertyName) {
return getStyle(textElement.id, propertyName)
};
// public functions and objects
this.getLines = function() {
return getLinesInString(getCurrentValue());
};
this.getGroups = function() {
return getGroupsInString(getCurrentValue());
};
this.setText = function(content) {
if (!content) {
content = this.sentData;
}
let hard = content.hard.join(lineSep);
let veryHard = content.very_hard.join(lineSep);
this.textElement.value = hard.concat("|" + lineSep, veryHard);
};
this.getLineHeight = function(element) {
if (!element) {
element = textElement;
}
let temp = document.createElement(element.nodeName);
temp.setAttribute("style", "margin:0px;padding:0px;font-family:" + element.style.fontFamily + ";font-size:" + element.style.fontSize);
temp.innerHTML = "test";
temp = element.parentNode.appendChild(temp);
let lineHeight = temp.clientHeight;
temp.parentNode.removeChild(temp);
return lineHeight;
};
this.getGroupItems = function() {
return getGroupItems();
};
this.textElement = textElement;
this.showme = showme;
};
let sentData = {
hard: [
"It's the possibility of having a dream come true that makes life interesting",
"I cannot fix on the hour, or the spot, or the look or the words, which laid the foundation."
],
very_hard: ["He stepped down, trying not to look long at her, as if she were the sun, yet he saw her, like the sun, even without looking."]
};
// create instances and use our app, pass the id
var containerApp = MyApp("textThing"); //default last three parameters
containerApp.sentData = sentData;
containerApp.setText();
let groups = containerApp.getGroups();
let groupItems = containerApp.getGroupItems();
containerApp.getMetrics();
containerApp.getGeometry();
// create instances and use our app, pass the id
var containerApp2 = MyApp("msgTwo", "showme", "|", "||");
//console.log("Second One Lines:", containerApp2.getLines().length);
//containerApp2.getMetrics();
//containerApp2.getGeometry();
.page-container {
display: flex;
/* center and stack the containers*/
justify-content: center;
flex-direction: column;
align-items: center;
font-size: 62.5%;
}
.container {
border: solid 1px black;
}
.container-block {
border: 2px dashed #AAAAAA;
}
.container-items {
width: 500px;
position: relative;
}
.container-items .format-custom,
.container-items label {
width: 100%;
}
.container-items .format-custom {
height: 10em
}
.message-hr {
border: 1px solid blue;
background-color: blue;
height: 0.05em;
width: 450px;
align-items: center;
margin: 0.5em;
}
.showme {
border: dotted 2px dodgerblue;
background-color: #EEEEEE;
padding: 1em;
}
textarea.format-custom,
.custom-underlay,
.underlay {
margin: 0;
box-sizing: border-box;
vertical-align: top;
display: block;
border: lime solid 1px;
}
textarea.format-custom {
width: 100%;
height: 3em;
background: transparent;
resize: none;
border: red solid 1px;
padding: 0.5em;
}
.underlay {
border: 1px solid #fff;
width: 100%;
height: 100%;
background: transparent;
top: 1em;
left: 0;
display: block;
z-index: -1;
position: absolute;
}
.custom-underlay {
width: 100%;
height: 50%;
margin: 0;
box-sizing: border-box;
vertical-align: top;
position: absolute;
}
.custom-underlay.top {
background-color: #FFFF00;
top: 0;
left: 0;
}
.custom-underlay.bottom {
background-color: #FFAAAA;
top: 50%;
left: 0;
}
<div class="page-container">
<div class="container">
<div class="container-block">
<div class="container-items">
<label for="textThing">Your message:</label>
<textarea id="textThing" name="textThing" class="format-custom">howdy, I am here|another one | cheese burgers
fries and a drink
||
bottom of here| bottom second</textarea>
<div class="underlay">
<div class="custom-underlay top"></div>
<div class="custom-underlay bottom"></div>
</div>
</div>
</div>
<div class="showme">xxxone</div>
</div>
<div class="container">
<div class="container-block">
<div class="message-hr container-items">
</div>
</div>
</div>
<div class="container">
<div class="container-block">
<div class="container-items">
<label for="msgTwo">Second message:</label>
<textarea id="msgTwo" name="msgTwo" class="format-custom">Not the same|Nxxt one
||
bottom of next</textarea>
<div class="underlay">
<div class="custom-underlay top"></div>
<div class="custom-underlay bottom"></div>
</div>
</div>
</div>
<div class="showme">xxxtwo</div>
</div>
</div>
its not with color but in this code you can set a lable like this (hard: veryhard :)
state = {
value: "",
hard: [],
veryHard: []
};
handleChange = ({ target }) => {
const { value, name } = target;
console.log(value, name);
this.setState({ value });
};
performAnalysis = () => {
let hard = [
//this state ,get from performHard function (you should set state it )
"It's the possibility of having a dream come true that makes life interesting",
"I cannot fix on the hour, or the spot, or the look or the words, which laid the foundation."
];
let veryHard = [
//this state ,get from performVHard function (you should set state it )
"“He stepped down, trying not to look long at her, as if she were the sun, yet he saw her, like the sun, even without looking.“"
];
this.setState({ hard, veryHard });
// you shoud setState like this in these 2 function
// this.performHard();
// this.performVHard();
};
render() {
return (
<div className="App">
<header className="App-header">
<textarea
value={
this.state.value.length
? this.state.value
: " Hard : " +
" " +
this.state.hard +
" " +
"very Hard : " +
this.state.veryHard
}
onChange={this.handleChange}
/>
<button onClick={this.performAnalysis}>analise</button>
</header>
</div>
);
}
its not exactly that you want,but you can get help from this code
I have created a set of dots using div tags inside a div tag. My need is when I drag the last dot, the whole set of dots should move and sit where mouse pointer is placed at present. I tried achieving it using addeventlistner for mouse clicks but failed in my attempt.
Can someone point out the intuition in the segment below?
var dots = document.createElement("div");
dots.className = "dots";
document.body.appendChild(dots);
var dotarray = [];
for (index = 0; index < 10; index++) {
dotarray[index] = document.createElement("div");
dotarray[index].className = "dot";
dots.appendChild(dotarray[index]);
}
dotarray[9].addEventListener("mousedown", function(event) {
if (event.which == 1) {
var currentMousePointerPos, latestMousePointerPos;
currentMousePointerPos = event.pageX;
dotarray[9].addEventListener("mouseup", function(event) {
latestMousePointerPos = event.pageX;
if (currentMousePointerPos != latestMousePointerPos) {
dots.style.marginLeft = currentMousePointerPos + latestMousePointerPos;
}
})
}
})
.dot {
width: 8px;
height: 8px;
border-radius: 4px;
background-color: red;
display: inline-block;
margin-left: 5px;
}
.dots {
border: 1px solid black;
width: 135px;
}
The immediate answer to your question is that dots.style.marginLeft needs to be equal to a string, containing the units.
Hence, this would work:
dots.style.marginLeft = ((currentMousePointerPos+latestMousePointerPos) + "px");
However:
Your mouseup listener only listens to the event that the mouse click is released when it's over the element, so it doesn't do much. If you assign the listener to the whole document, the listener's function would be activated no matter where the mouseup event occurres.
currentMousePointerPos + latestMousePointerPos doesn't represent the final position of the mouse.
If we fix these two issues the will code still operate weirdly, because the left side of the dots element is set to the mouse's last position.
Therefore we just need to subtract the element's width from the marginLeft property.
The following code combines everything I've mentioned:
var dots = document.createElement("div");
dots.className = "dots";
document.body.appendChild(dots);
var dotarray = [];
for (index = 0; index < 10; index++) {
dotarray[index] = document.createElement("div");
dotarray[index].className = "dot";
dots.appendChild(dotarray[index]);
}
dotarray[9].addEventListener("mousedown", function(event) {
if (event.which == 1) {
var currentMousePointerPos;
// Notice how the listener is bound to the whole document
document.addEventListener("mouseup", function(event) {
currentMousePointerPos = event.pageX;
dots.style.marginLeft = ((currentMousePointerPos-dots.offsetWidth) + "px");
})
}
})
.dot {
width: 8px;
height: 8px;
border-radius: 4px;
background-color: red;
display: inline-block;
margin-left: 5px;
}
.dots {
border: 1px solid black;
width: 135px;
}
Is this what you are looking for?
You should use the mousemove event instead to detect any dragging
after mousedown.
var dots = document.createElement("div");
dots.className = "dots";
document.body.appendChild(dots);
var dotarray = [];
for (index = 0; index < 10; index++) {
dotarray[index] = document.createElement("div");
dotarray[index].className = "dot";
dots.appendChild(dotarray[index]);
}
dotarray[9].addEventListener("mousedown", function(event) {
if (event.which == 1) {
window.addEventListener('mousemove', move, true);
/*var currentMousePointerPos, latestMousePointerPos;
currentMousePointerPos = event.pageX;
dotarray[9].addEventListener("mouseup", function(event) {
latestMousePointerPos = event.pageX;
if (currentMousePointerPos != latestMousePointerPos) {
dots.style.marginLeft = currentMousePointerPos + latestMousePointerPos;
}
})*/
}
});
window.addEventListener("mouseup", function(event) {
window.removeEventListener('mousemove', move, true);
});
function move(e){
var div = document.getElementsByClassName('dots')[0];
div.style.position = 'absolute';
div.style.top = -8+e.clientY + 'px';
div.style.left = -135+8+e.clientX + 'px';
}
.dot {
width: 8px;
height: 8px;
border-radius: 4px;
background-color: red;
display: inline-block;
margin-left: 5px;
}
.dots {
border: 1px solid black;
width: 135px;
}
I'm new to JavaScript but moving over from ActionScript, so I'm using a lot of AS3 logic and not sure what's possible and not.
I have a series of 5 dots for an image slider nav. The dots are just CSS styled dots, so I'm trying to make it so I can control the colors using element.style.backgroundColor.
Here's my script:
function btnFeatured(thisBtn) {
btnFeatured_reset();
for (i = 1; i <= 5; i++) {
if (thisBtn === document.getElementById("dotFeat" + i)) {
document.getElementById("dotFeat" + i).style.backgroundColor = "#ffae00";
}
}
}
function btnFeatured_reset() {
for (i = 1; i <= 5; i++) {
document.getElementById("dotFeat" + i).style.backgroundColor = "#969696";
}
}
Seems to work just fine, but when I click the dot, it turns orange (ffae00) and then immediately turns back to gray (969696).
And just in case, here's the style I'm using for the dots:
#featured-nav a {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 8px;
background-color: #969696;
border-bottom: none;
margin: 0 14px;
}
#featured-nav a:hover {
background-color: #ffae00;
border-bottom: none;
}
And my html:
Change the HTML to
test
test
test
test
test
and the JS:
function btnFeatured(thisBtn) {
for (i = 1; i <= 5; i++) {
var state = parseInt(thisBtn.id.slice(-1),10) == i,
elem = document.getElementById("dotFeat" + i);
elem.style.backgroundColor = (state ? "#ffae00" : "#969696");
}
return false;
}
FIDDLE
Even better would be to not use inline JS, but proper event handlers.