How to solve this JS SyntaxError? - javascript

I'm new to JS and I'm trying some stuff to learn it. Here I'm stuck with this SyntaxError. I know it's supposed to point to an identifier starting with a digit, but there's none... So where's the problem ? Could you please help me ?
var mouse = document.getElementById('square');
SyntaxError: identifier starts immediately after numeric literal
div.onmouseover = function() {
var posL = 275;
var posV = 275;
var time = setInterval(move, 2);
function move() {
if ((posL==275)&&(posV==275)){
box.style.left = 275px;
box.style.top = 0px;
posV = 0;
}
}
}

Javascript is trying to parse 275px and 0px as code. It recognizes numbers like 275 as a numerical value but is puzzled by the px attached to it. You should use strings here, in JS they are delimited with ' or ".
Corrected code:
var mouse = document.getElementById('square');
//SyntaxError: identifier starts immediately after numeric literal
div.onmouseover = function() {
var posL = 275;
var posV = 275;
var time = setInterval(move, 2);
function move() {
if ((posL==275)&&(posV==275)){
box.style.left = '275px';
box.style.top = '0px';
posV = 0;
}
}
}

You're missing the qoutes:
box.style.left = "275px";
box.style.top = "0px";
Also, in div.onmouseover you need to define div first or you probably mean mouse.onmouseover

Related

extract a number from a property value

I am trying to create an animation, when a button is pressed, the div have to move to left 300 px by changing the left-margin,and stop.
I get the left margin value, but when I try to extract 300 px from it, it says that the var moveto is not a number, and current_left_margin`s value is 0px, how can I get this value to be a number, without the px?
here is my full code:
function get_vars() {
elem = document.getElementById('front-slide');
}
window.onload = get_vars;
function vb_anim() {
var interval = setInterval(anim, 10);
var elemstyle = window.getComputedStyle(elem);
var elemvidth = elemstyle.width;
var current_left_margin = elemstyle.getPropertyValue('margin-left');
var moveto = current_left_margin - 300;
console.log('current_left_margin= ' + current_left_margin );
console.log('moveto= ' + moveto );
var pos = 0;
function anim() {
if (pos == moveto) {
clearInterval(interval);
} else {
pos--;
elem.style.marginLeft = pos + 'px';
}
}
}
This should do:
var current_left_margin = parseInt(elemstyle.getPropertyValue('margin-left'));
If not, then:
var current_left_margin = parseInt(elemstyle.getPropertyValue('margin-left').replace('px', ''));

highlighting and editing text in long string

In a HTML/JavaScript/React/Redux web application, I have a long string (around 300kb) of natural language. It is a transcript of a recording being played back.
I need
to highlight the currently uttered word,
to recognize a word that's clicked on,
to extract selected ranges
and to replace parts of the string (when a correction to the transcript is submitted by the user).
Everything is easy when I wrap each word in its own <span>. However, this makes the number of elements unbearable for the browser and the page gets very slow.
I can think of two ways to approach this:
I could wrap each sentence in a <span> and only wrap each word of the currently played-back sentence.
I could leave the text without HTML tags, handle clicks via document.caretPositionFromPoint, but I don't know how to highlight a word.
I would welcome more ideas and thoughts on the balance between difficulty and speed.
"to recognize a word that's clicked on"
New answer
I figure that, the code in my previous answer actually had to split the huge string of text into an huge array on every on click event. After that, a linear search is performed on the array to locate the matching string.
However, this could be improved by precomputing the word array and use binary search instead of linear searching.
Now every highlighting will run in O(log n) instead of O(n)
See: http://jsfiddle.net/amoshydra/vq8y8h19/
// Build character to text map
var text = content.innerText;
var counter = 1;
textMap = text.split(' ').map((word) => {
result = {
word: word,
start: counter,
end: counter + word.length,
}
counter += word.length + 1;
return result;
});
content.addEventListener('click', function (e) {
var selection = window.getSelection();
var result = binarySearch(textMap, selection.focusOffset, compare_word);
var textNode = e.target.childNodes[0];
if (textNode) {
var range = document.createRange();
range.setStart(textNode, textMap[result].start);
range.setEnd(textNode, textMap[result].end);
var r = range.getClientRects()[0];
console.log(r.top, r.left, textMap[result].word);
// Update overlay
var scrollOffset = e.offsetY - e.clientY; // To accomondate scrolling
overlay.innerHTML = textMap[result].word;
overlay.style.top = r.top + scrollOffset + 'px';
overlay.style.left = r.left + 'px';
}
});
// Slightly modified binary search algorithm
function binarySearch(ar, el, compare_fn) {
var m = 0;
var n = ar.length - 1;
while (m <= n) {
var k = (n + m) >> 1;
var cmp = compare_fn(el, ar[k]);
if (cmp > 0) {
m = k + 1;
} else if(cmp < 0) {
n = k - 1;
} else {
return k;
}
}
return m - 1;
}
function compare_word(a, b) {
return a - b.start;
}
Original answer
I took a fork of code from this answer from aaron and implemented this:
Instead of setting a span tag on the paragraph, we could put an overlay on top of the word.
And resize and reposition the overlay when travelling to a word.
Snippet
JavaScript
// Update overlay
overlayDom.innerHTML = word;
overlayDom.style.top = r.top + 'px';
overlayDom.style.left = r.left + 'px';
CSS
Use an overlay with transparent color text, so that we can get the overlay to be of the same width with the word.
#overlay {
background-color: yellow;
opacity: 0.4;
display: block;
position: absolute;
color: transparent;
}
Full forked JavaScript code below
var overlayDom = document.getElementById('overlay');
function findClickedWord(parentElt, x, y) {
if (parentElt.nodeName !== '#text') {
console.log('didn\'t click on text node');
return null;
}
var range = document.createRange();
var words = parentElt.textContent.split(' ');
var start = 0;
var end = 0;
for (var i = 0; i < words.length; i++) {
var word = words[i];
end = start+word.length;
range.setStart(parentElt, start);
range.setEnd(parentElt, end);
// not getBoundingClientRect as word could wrap
var rects = range.getClientRects();
var clickedRect = isClickInRects(rects);
if (clickedRect) {
return [word, start, clickedRect];
}
start = end + 1;
}
function isClickInRects(rects) {
for (var i = 0; i < rects.length; ++i) {
var r = rects[i]
if (r.left<x && r.right>x && r.top<y && r.bottom>y) {
return r;
}
}
return false;
}
return null;
}
function onClick(e) {
var elt = document.getElementById('info');
// Get clicked status
var clicked = findClickedWord(e.target.childNodes[0], e.clientX, e.clientY);
// Update status bar
elt.innerHTML = 'Nothing Clicked';
if (clicked) {
var word = clicked[0];
var start = clicked[1];
var r = clicked[2];
elt.innerHTML = 'Clicked: ('+r.top+','+r.left+') word:'+word+' at offset '+start;
// Update overlay
overlayDom.innerHTML = word;
overlayDom.style.top = r.top + 'px';
overlayDom.style.left = r.left + 'px';
}
}
document.addEventListener('click', onClick);
See the forked demo: https://jsfiddle.net/amoshydra/pntzdpff/
This implementation uses the createRange API
I don't think the number of <span> elements is unbearable once they have been positioned. You might just need to minimize reflow by avoiding layout changes.
Small experiment: ~3kb of text highlighted via background-color
// Create ~3kb of text:
let text = document.getElementById("text");
for (let i = 0; i < 100000; ++i) {
let word = document.createElement("span");
word.id = "word_" + i;
word.textContent = "bla ";
text.appendChild(word);
}
document.body.appendChild(text);
// Highlight text:
let i = 0;
let word;
setInterval(function() {
if (word) word.style.backgroundColor = "transparent";
word = document.getElementById("word_" + i);
word.style.backgroundColor = "red";
i++;
}, 100)
<div id="text"></div>
Once the initial layout has finished, this renders smoothly for me in FF/Ubuntu/4+ years old laptop.
Now, if you where to change font-weight instead of background-color, the above would become unbearably slow due to the constant layout changes triggering a reflow.
Here is a simple editor that can easily handle very large string. I tried to use minimum DOM for performance.
It can
recognize a word that's clicked on
highlight the currently clicked word, or drag selection
extract selected ranges
replace parts of the string (when a correction to the transcript is submitted by the user).
See this jsFiddle
var editor = document.getElementById("editor");
var highlighter = document.createElement("span");
highlighter.className = "rename";
var replaceBox = document.createElement("input");
replaceBox.className = "replace";
replaceBox.onclick = function() {
event.stopPropagation();
};
editor.parentElement.appendChild(replaceBox);
editor.onclick = function() {
var sel = window.getSelection();
if (sel.anchorNode.parentElement === highlighter) {
clearSelection();
return;
}
var range = sel.getRangeAt(0);
if (range.collapsed) {
var idx = sel.anchorNode.nodeValue.lastIndexOf(" ", range.startOffset);
range.setStart(sel.anchorNode, idx + 1);
var idx = sel.anchorNode.nodeValue.indexOf(" ", range.endOffset);
if (idx == -1) {
idx = sel.anchorNode.nodeValue.length;
}
range.setEnd(sel.anchorNode, idx);
}
clearSelection();
range.surroundContents(highlighter);
range.detach();
showReplaceBox();
event.stopPropagation();
};
document.onclick = function(){
clearSelection();
};
function clearSelection() {
if (!!highlighter.parentNode) {
replaceBox.style.display = "none";
highlighter.parentNode.insertBefore(document.createTextNode(replaceBox.value), highlighter.nextSibling);
highlighter.parentNode.removeChild(highlighter);
}
editor.normalize(); // comment this line in case of any performance issue after an edit
}
function showReplaceBox() {
if (!!highlighter.parentNode) {
replaceBox.style.display = "block";
replaceBox.style.top = (highlighter.offsetTop + highlighter.offsetHeight) + "px";
replaceBox.style.left = highlighter.offsetLeft + "px";
replaceBox.value = highlighter.textContent;
replaceBox.focus();
replaceBox.selectionStart = 0;
replaceBox.selectionEnd = replaceBox.value.length;
}
}
.rename {
background: yellow;
}
.replace {
position: absolute;
display: none;
}
<div id="editor">
Your very large text goes here...
</div>
I would first find the clicked word via some annoying logic (Try looking here )
Then you can highlight the word simply by wrapping the exact word with a styled span as you suggested above :)
Well, I'm not really sure how you could recognise words. You may need a 3rd party software. To highlight a word, you can use CSS and span as you said.
CSS
span {
background-color: #B6B6B4;
}
To add the 'span' tags, you could use a find and replace thing. Like this one.
Find: all spaces
Replace: <span>

Randomize background img with JS

I have working code, that randomly changes background of the body with my gif's.
var totalCount = 11;
function ChangeIt() {
var num = Math.ceil( Math.random() * totalCount );
document.body.background = '/static/img/'+num+'.gif';
document.body.style.backgroundRepeat = "repeat";
}
window.onload = ChangeIt;
Now I want to use it with another HTML element, one of the div's, for example. Changing document.body.background to document.getElementById('id').background doesn't work. How should I change the code?
document.body.background mostly worked for body background but not other html elements.So used this document.getElementById('id').style.backgroundImage = "url(..)";
var totalCount = 11;
function ChangeIt() {
var divs =document.getElementById('id');
var num = Math.ceil( Math.random() * totalCount );
divs.style.backgroundImage = 'url(/static/img/'+num+'.gif)';
divs.style.backgroundRepeat = "repeat";
divs.style.height = "300px"; //testing
}
window.onload = ChangeIt;
Have you tried this?
document.getElementById('id').style.backgroundImage = "url('/static/img/" + num + ".gif')";
Also, the RNG with Math.ceil() means that images are based on 1 not on 0:
1.gif
2.gif
...
11.gif
If this is not the case use Math.floor():
0.gif
1.gif
...
10.gif
Depending on what the element is, you may need to set its dimensions (width and height). This is an example HTML element:
<div id="imagediv" style="width: 320px; height: 640px; background-repeat: repeat;"></div>
This the resulting code:
var totalCount = 11;
window.onload = function () {
var num, node;
num = Math.ceil(Math.random() * totalCount);
node = document.getElementById("imagediv");
node.style.backgroundImage = "url('/static/img/" + num + ".gif')";
};
this should be the correct syntax:
document.getElementById('id').style.backgroundImage = "url('img_tree.png')";

the if statement do not work in javascript

I want to have a try with the clearInterval() method in some conditions.
But it seems do not work anyway.
window.onload = function(){
var list = document.getElementById("list");
list.style.position = "relative";
list.style.left = 0;
var move = function(){
list.style.left = parseInt(list.style.left) + 200 + "px";
demo[0].innerHTML = parseInt(list.style.left);
}
var myVar = setInterval(move,1000);
if (parseInt(list.style.left) == 600) {
clearInterval(myVar);
}
}
I don't konw why there is nothing happened when the value of left property is "600".
Thank you very much for help.
The way it's written now, move hasn't been called yet, so this code has nothing to check:
if (parseInt(list.style.left) == 600) {
clearInterval(myVar);
}
Instead, add that condition inside of move() and have it end its own execution:
var myVar; // change scope to outside of the below function.
window.onload = function(){
var list = document.getElementById("list");
list.style.position = "relative";
list.style.left = 0;
var move = function(){
list.style.left = parseInt(list.style.left) + 200 + "px";
demo[0].innerHTML = parseInt(list.style.left);
if (parseInt(list.style.left) == 600) {
clearInterval(myVar);
}
}
myVar = setInterval(move,1000);
}
also probably want to make the scope more global (or at least make it more evident it can be modified outside of the below function). It will be hoisted anyways, but making it more clear will help maintenance later on.
Your if statement is only called once in the onload event. After that, it never gets called again, so it never has a chance to clear the interval. If you changed your move function to something like this:
var move = function(){
list.style.left = parseInt(list.style.left) + 200 + "px";
demo[0].innerHTML = parseInt(list.style.left);
// Check to see if we should clear our interval
if (parseInt(list.style.left) == 600) {
clearInterval(myVar);
}
}
That'll do the check every time move is called. If the conditional inside the if statement evals to true, it'll clear your interval.
You can do something like this:
window.onload = function(){
var list = document.getElementById("list");
list.style.position = "relative";
list.style.left = 0;
var move = function(){
if (parseInt(list.style.left) == 600) {
clearInterval(myVar);
}
list.style.left = parseInt(list.style.left) + 200 + "px";
demo[0].innerHTML = parseInt(list.style.left);
}
var myVar = setInterval(move,1000);
}
list.style.left returns a string which could be followed whith a unit (px, pt ....). It could even be a percentage.

How do I transition the changing of CSS values?

I'm using JavaScript to change CSS values to make a particular div fill the page when a button is clicked. But I would like make the change from small to filling the screen smooth. How do I do this with CSS or Javascript? This is currently how I'm changing the size of that div
function fullscreen(){ // called when button is clicked
var d = document.getElementById('viewer').style;
if(!isFullscreen){ // if not already fullscreen change values to fill screen
d.width = "100%";
d.height="100%";
d.position= "absolute";
d.left="0%";
d.top="0%";
d.margin="0 0 0 0";
isFullscreen = true;
}else{ // minimizie it
d.width="600px";
d.height="400px";
d.margin="0 auto";
d.position="relative";
isFullscreen = false;
}
}
How do I code the change from the full screen values to the minimized values to be a smooth transition instead of instantaneous?
Use jQuery'sanimate() function!
For example:
function fullscreen(){ // called when button is clicked
var o = {} // options
var speed = "fast"; // You can specify another value
if(!isFullscreen){ // if not already fullscreen change values to fill screen
o.width = "100%";
o.height="100%";
o.left="0%";
o.top="0%";
o.margin="0 0 0 0";
$("#viewer").animate(o,speed);
isFullscreen = true;
}else{ // minimize it
o.width="600px";
o.height="400px";
o.margin="0 auto";
$("#viewer").animate(o,speed);
isFullscreen = false;
}
}
You can do this by using Jquery, .animate() API see the reference .animate()
I have created a small demo using .animate() click the Demo to see the example.
What you want to do is rather complicated, first you need to get the absolute position and dimension of your element in the document, also the dimension of the document itself, there is no native cross-platform javascript functions for that but there are known techniques to find out those values, do a search. So assuming you will implement these functions yourself: getAbsoluteLeft(), getAbsoluteTop(), getWidth(), getHeight(), getDocWidth() and getDocHeight() here is the animating code (not tested):
function fullscreen(){ // called when button is clicked
var e = document.getElementById('viewer');
var d = e.style;
if(!isFullscreen){ // if not already fullscreen change values to fill screen
var duration = 1000 //milliseconds
var framesPerSecond = 24;
var beginLeft = getAbsoluteLeft( e );
var beginTop = getAbsoluteTop( e );
var beginRight = beginLeft + getWidth( e );
var beginBottom = beginTop + getHeight( e );
var endLeft = 0;
var endTop = 0;
var endRight = getDocWidth();
var endBottom = getDocHeight();
var totalFrames = duration / (1000/framesPerSecond);
var frameNo = 0;
var leftStep = (beginLeft - endLeft) / totalFrames;
var topStep = (beginTop - endTop) / totalFrames;
var rightStep = (endRight - beginRight) / totalFrames;
var bottomStep = (endBottom - beginBottom) / totalFrames;
var func = function () {
var left = beginLeft - leftStep * frameNo;
var top = beginTop - topStep * frameNo;
d.left = left+'px';
d.top = top+'px';
d.width = (beginRight + rightStep * frameNo - left)+'px';
d.height = (beginBottom + bottomStep * frameNo - top)+'px';
++frameNo;
if( frameNo == totalFrames ) {
clearInterval( timer );
d.width = "100%";
d.height="100%";
d.left="0%";
d.top="0%";
isFullscreen = true;
}
}
d.position= "absolute";
d.margin="0 0 0 0";
timer = setInterval( func, 1000 / framesPerSecond );
} else { // minimizie it
d.width="600px";
d.height="400px";
d.margin="0 auto";
d.position="relative";
isFullscreen = false;
}
}

Categories

Resources