Creating a content assistant (ctrl+space) using Javascript and editable document - javascript

I've created a running example of a content assistant in an editable area on a html document. So if the user hits ctrl and space on the keyboard a context menu appears. Currently (see demo bellow) the context menu is on the right y position (bellow the text). But it goes not along with the x-axis (if the text becomes longer the box will be sown on the beginning of the line).
Can you help me solving this problem?
Greetings,
mythbu
Example code:
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Test</title>
<script type="text/javascript">
var iframe = null, iwindow = null, iDocument = null;
function setUpInput() {
iframe = document.createElement( 'iframe' );
iframe.setAttribute( 'id', 'iframe-test' );
iframe.setAttribute( 'frameborder', 0 );
iframe.setAttribute( 'style', 'width:100%; height:100%;border: solid 1px red;' );
document.getElementById( "input" ).appendChild( iframe );
iwindow = iframe.contentWindow;
idocument = iwindow.document;
idocument.open();
idocument.write("<p></p>");
idocument.close();
idocument.body.setAttribute( 'spellcheck', false );
idocument.body.setAttribute( 'style', 'font-family: Consolas,serif;font-size: 0.8em;' );
idocument.body.contentEditable = true;
iwindow.onkeydown = function(e) {
if (e.ctrlKey && e.keyCode == 32) {
createSuggestObject();
return false;
}
if (e.ctrlKey) return false;
};
iwindow.onkeypress = function(e) {if (e.ctrlKey) return false;};
}
function createSuggestObject() {
suggest = new Object();
suggest.box = document.createElement( 'div' );
suggest.box.style.position = 'absolute';
suggest.box.style.width = '120px';
suggest.box.style.overflow = 'auto';
suggest.box.style.border = '1px solid #BEC7E4';
suggest.box.style.display = 'block';
suggest.box.style.marginTop = '16px';
suggest.box.innerHTML = "Example 1";
document.body.appendChild( suggest.box )
var position = iframe.getBoundingClientRect();
var selObj = iwindow.getSelection();
var selRange = selObj.getRangeAt(0);
var p2 = selObj.anchorNode.parentNode.getBoundingClientRect();
suggest.box.style.top = Math.round( window.scrollY + position.top + p2.top) + 'px';
suggest.box.style.left = Math.round( window.scrollX + position.left + p2.left) + 'px';
}
window.onload = function() {
setUpInput();
};
</script>
</head>
<body>
<div id="input"></div>
</body>
</html>

Main problem with your solution was that you were using bounding rectangle of the p element (which as you assumed contained text inputed by user) as a reference to where to place suggest object.
First of all you didn't account bounding rectangle width into the position of the suggest object, so your suggestion box stayed on the left no matter how long the text was.
However, that approach would fail eventually because if the text would have more than one line (where the second line would be shorter than the first one), the width of bounding rectangle would equal length of the first line (more or less). Hence, the suggestion object would be positioned incorrectly.
My first idea of how to fix your code was to append some inline element to the text (beacon, if you will), measure it's position, remove it from the DOM and use that calculated position to properly set suggestion object.
It turned out to almost work. Almost, because as it turned out, different browsers use different methods of dealing with contentEditable line endings. Firefox for example inserts <br _moz_dirty=""/> at the end of the line, while Chrome does not. So when I tried to append my beacon element after the text it was appended after that <br/> causing incorrect position again.
Solution was to change the way of getting text node we're dealing with and insert beacon right before nextSibling of it.
Here's working example http://jsfiddle.net/MmKXS/10/
Note 1: I've removed addition of empty <p></p> element to the document of the iframe since in Chrome text inputed by user wasn't inserted into it, causing yet another problems with positioning suggest object.
Note 2: As for now my solution only works for positioning suggestion object at the end of the text, not at the cursor position as it would involve splitting textNodes, inserting the beacon, checking its position and merging textNodes again. Depending on use case you have for that code that could lead to poor performance and/or could require changing the whole approach of how to deal with positioning your suggestion object.

Related

How do you get the color of a specific pixel on a webpage in javascript? (no canvas)

I have been trying to figure out a way to find the color of a pixel on my webpage. As I have searched for an answer, all I have found is how to do this in a canvas element. Is there a way to do this for the entire page, and if so, how?
Here is an example piece of code that shows a basic part of what I am doing.
//this executes whenever the mouse is moved
document.addEventListener('mousemove', (event) => {
//this just simplifies the varables
mouseX = event.clientX;
mouseY = event.clientY;
document.getElementById("info").innerHTML = "Position: ( " + mouseX + " , " + mouseY + " ) | Color: " + getColor();
// What command do you use to do this? Is it related to the document object? ^
});
function getColor() {
return (" rgb ( 2, 5, 7 ) ");
};
<!DOCTYPE html>
<html>
<body>
<p id="info"><br></p>
<img width="250" src="https://pngimg.com/uploads/chicken/chicken_PNG2160.png"/>
</body>
</html>
I don't know if this is best practice, but you could set up all the elements on the page with a mouseover event that would tell your app what element being hovered. The app could check the element to find out what color it is.

Change CSS of element within elements and classes

I am trying to set the height of an image to be 50% of the width, but the image scales with the page. The current CSS I am targeting looks like this:
.img-quiz p span img { width: 100%; }
Here's what I've tried in js, but isn't working:
var imgQuiz = document.getElementByClassName('img-quiz').getElementsByTagName('img');
var elementStyle = window.getComputedStyle(imgQuiz);
var pixHeight = elementStyle.getPropertyValue('height');
imgQuiz.style.height = pixHeight * .5;
Also, I'm new to javascript, do I need to wrap this in a function (i.e. window.onload = function())?
Check out this JS Fiddle for help...
https://jsfiddle.net/Lm60v949/6/
var image = document.getElementsByClassName('img-quiz')[0].getElementsByTagName('img')[0];
// console.log( image ); // test to make sure the image was captured
/*
var image = document.getElementById('imgQuiz'); // much cleaner way to select
*/
image.height = image.width / 2;
// console.log( image.height, image.width ); // check the results
.img-quiz p span img { width: 100%; }
<div class="img-quiz">
<p>
<span>
<img id="imgQuiz" src="https://image.shutterstock.com/z/stock-photo--week-old-cocker-spaniel-puppy-630877553.jpg">
</span>
</p>
</div>
A couple of hiccups here:
.getElementsByClassName and .getElementsByTagName both return a HTMLCollection -- to use these, you will have to select the index of the collection that matches your image ([0], [1], [2], ... )
If you can, an ID on the targeted image would be ideal. Just easier to write and read code.
var image = document.getElementsByClassName('img-quiz')[0].getElementsByTagName('img')[0];
If you want brownie points, you can do something like:
image.onload = function(){
image.height = image.width / 2;
};
(assuming you have the image already captured and saved to the variable "image")
This is a nice sanity check to make sure the image exists (and has a height & a width) before trying to manipulate it.
You may be overthinking the problem a bit -- once an image is loaded, you can just look up the width and set the height property of the image.
There's a couple of extras that you can do here:
Check to make sure that the image width is not 0 (zero) ... this is a nice double-check that the image has loaded
if( image.width > 0 ){ ... }
Round the number down to a whole number (there's nothing wrong with .5 size increments, just nice to work with whole numbers)
image.height = Math.floor( image.width/2 );
Summary of your question:
My understanding of your situation is, your trying to make an image have the height of 50% of the webpage but its not doing so, you have looked into different methods including JavaScript but struggling, also you asked about onload methods.
If so this is my proposed solution
Solution
In this example we are using pure JavaScript (no library or framework)
The html has pre-set the src for the image, if you want to dynamically set the src then use the following imgEl.src = "path/to/file.jpg";
The method or trigger used is a DOMContentLoaded, which ensures all the HTML elements of the page have loaded before it begins trying to read and manipulate the DOM Elements.
The function we trigger is called funStart (Short for function Start), I always encourage people to use abreviations that define the object type so that it is easier to read such as fun for function, str for string, bl for boolean, obj for object, int for integer so on.
inside funStart we are assigning an DOM element as imgEl which is an image obj and we are saying set the width to be innerWidth which is the document width
We are then saying set the height to be 50% of the document height (innerHeight), by dividing the value into 2.
function funStart(){
var imgEl = document.getElementById("targetImg");
imgEl.height = innerHeight / 2;
imgEl.width = innerWidth;
}
document.addEventListener("DOMContentLoaded", funStart, false);
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<img id="targetImg" src="https://static.pexels.com/photos/257360/pexels-photo-257360.jpeg" alt="background image" title="background">
</body>
</html>
Here's a simple example (you can learn more in here):
var image = document.getElementsByClassName('img-quiz')[0];
image.height = image.width / 2;
<img src="https://dummyimage.com/100x100">
<img class="img-quiz" src="https://dummyimage.com/100x100">
The HTML file would be like this:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<img src="https://dummyimage.com/100x100">
<img class="img-quiz" src="https://dummyimage.com/100x100">
<script>
// you could also move the script tag to the head and add an event handler to the event load (window.onload = function() {...};
var image = document.getElementsByClassName('img-quiz')[0];
image.height = image.width / 2;
</script>
</body>
</html>

making box edge follow mouse pointer in javascript

I can't figure out why this doesn't work. What I have here is my own black canvas made from a DIV. In that canvas, I want the user to define the first point which I'm successful at, but after clicking the first point, when the mouse moves, the box must size properly and follow the mouse, much like drawing a rectangular box in a paint program. This is where I have difficulty.
Is there a way I can solve this such that it works at minimum and without using Jquery? all the better if I can get a solution for Internet Explorer 7 (or 8 at least).
<div ID="CANVAS" style="background:#000;width:600px;height:400px"></div>
<script>
var startx=-1,starty=-1,points=0,box;
var canvas=document.getElementById('CANVAS');
canvas.onclick=dopoint;
canvas.onmousemove=sizebox;
function dopoint(e){
if (points==0){
var area=canvas.getBoundingClientRect();
box=document.createElement('DIV');
box.style.position='relative';
box.style.border='2px solid yellow';
canvas.appendChild(box);
startx=e.clientX-area.left;
starty=e.clientY-area.top;
box.style.left=startx+'px';
box.style.top=starty+'px';
box.style.width='10px';
box.style.height='10px';
}
points=1-points;
}
function sizebox(e){
if (points==1){
var x=e.clientY,y=e.clientY; //here I'm thinking subtract old point from new point to get distance (for width and height)
if (x>startx){
box.style.left=startx+'px';
box.style.width=(x-startx)+'px';
}else{
box.style.left=x+'px';
box.style.width=(startx-x)+'px';
}
if (y>starty){
box.style.top=starty+'px';
box.style.height=(y-starty)+'px';
}else{
box.style.top=y+'px';
box.style.height=(starty-y)+'px';
}
}
}
</script>
Your code was almost good, except few small things. I have corrected it and wrote some comments on the lines that I've changed.
https://jsfiddle.net/1brz1gpL/3/
var startx=-1,starty=-1,points=0,box;
var canvas=document.getElementById('CANVAS');
canvas.onclick=dopoint;
canvas.onmousemove=sizebox;
function dopoint(e){
if (points==0){
var area=canvas.getBoundingClientRect();
box=document.createElement('DIV');
box.style.position='absolute'; // here was relative and changed to absolute
box.style.border='2px solid yellow';
canvas.appendChild(box);
startx=e.clientX; // removed -area.left
starty=e.clientY; // removed -area.right
box.style.left=startx+'px';
box.style.top=starty+'px';
box.style.width='0px'; // updated to 0px instead of 10 so it won't "jump" after you move the mouse with less then 10px
box.style.height='0px'; // same
}
points=1-points;
}
function sizebox(e){
if (points==1){
var x=e.clientX,y=e.clientY; // here was x = e.clientY and changed to x = clientX
if (x>=startx){
box.style.left=startx+'px';
box.style.width=(x-startx)+'px'; // here it was x+startx and changed to x-startx
}else{
box.style.left=x+'px';
box.style.width=(startx-x)+'px';
}
if (y>starty){
box.style.top=starty+'px';
box.style.height=(y-starty)+'px';
}else{
box.style.top=y+'px';
box.style.height=(starty-y)+'px';
}
}
}

JavaScript filter hueRotate does not work

I am trying to change an image's (#bgImage) hue when hovering over a button (profIcon) in JavaScript. This type of button is created with JS, and there are 9 of them.
Here is the code that creates a DOM image element, sets it's source, position, size and transition. Finally it appends it to it's container. The image appears correctly, where it should be.
I am using quickID in place of document.getElementById, and it is working without error.
var bgImage = document.createElement("img");
bgImage.id = "bgImage";
bgImage.src = "./resources/images/backgrounds/profession/selector.jpg";
bgImage.style.position = "absolute";
bgImage.style.left = "0px";
bgImage.style.top = "0px";
bgImage.style.width = "100%";
bgImage.style.height = "100%";
bgImage.style.zIndex = 1;
bgImage.style.webkitTransition = "all 0.5s ease";
quickID("centerdiv").appendChild(bgImage);
Here is the code that runs when I hover over an image:
profIcon.onmouseover = function () {
var thisNr = this.id.substr(8); //last char of the profIcon ID; number between 0 and 8
var newHue = profTomb[thisNr][3]; //this contains the value and only that.
console.log(newHue); //always returns the correct value
quickID(this.id).style.webkitFilter = "grayscale(0%)"; //this part works, too
quickID("bgImage").style.webkitFilter = "hueRotate(" + newHue + "deg)";
}
My problem: for some reason, the filter does not apply. newHue is either a positive (75), or a negative (-23) value and it's inserted correctly, as it appears in the console log. I only use webkit vendor prefix as I use Google Chrome.
I waited up to 1 minute with my mouse cursor over the image, thinking my system needs time to process the transformation, but nothing happened.
Does anyone knows what is the problem?
The correct string to use is hue-rotate, not hueRotate. The following should work:
quickID("bgImage").style.webkitFilter = "hue-rotate(" + newHue + "deg)";

converting Div to Canvas options [duplicate]

It would be incredibly useful to be able to temporarily convert a regular element into a canvas. For example, say I have a styled div that I want to flip. I want to dynamically create a canvas, "render" the HTMLElement into the canvas, hide the original element and animate the canvas.
Can it be done?
There is a library that try to do what you say.
See this examples and get the code
http://hertzen.com/experiments/jsfeedback/
http://html2canvas.hertzen.com/
Reads the DOM, from the html and render it to a canvas, fail on some, but in general works.
Take a look at this tutorial on MDN: https://developer.mozilla.org/en/HTML/Canvas/Drawing_DOM_objects_into_a_canvas (archived)
Its key trick was:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var data = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' +
'<foreignObject width="100%" height="100%">' +
'<div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">' +
'<em>I</em> like ' +
'<span style="color:white; text-shadow:0 0 2px blue;">' +
'cheese</span>' +
'</div>' +
'</foreignObject>' +
'</svg>';
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svg = new Blob([data], {type: 'image/svg+xml;charset=utf-8'});
var url = DOMURL.createObjectURL(svg);
img.onload = function () {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
}
img.src = url;
That is, it used a temporary SVG image to include the HTML content as a "foreign element", then renders said SVG image into a canvas element. There are significant restrictions on what you can include in an SVG image in this way, however. (See the "Security" section for details — basically it's a lot more limited than an iframe or AJAX due to privacy and cross-domain concerns.)
Sorry, the browser won't render HTML into a canvas.
It would be a potential security risk if you could, as HTML can include content (in particular images and iframes) from third-party sites. If canvas could turn HTML content into an image and then you read the image data, you could potentially extract privileged content from other sites.
To get a canvas from HTML, you'd have to basically write your own HTML renderer from scratch using drawImage and fillText, which is a potentially huge task. There's one such attempt here but it's a bit dodgy and a long way from complete. (It even attempts to parse the HTML/CSS from scratch, which I think is crazy! It'd be easier to start from a real DOM node with styles applied, and read the styling using getComputedStyle and relative positions of parts of it using offsetTop et al.)
You can use dom-to-image library (I'm the maintainer).
Here's how you could approach your problem:
var parent = document.getElementById('my-node-parent');
var node = document.getElementById('my-node');
var canvas = document.createElement('canvas');
canvas.width = node.scrollWidth;
canvas.height = node.scrollHeight;
domtoimage.toPng(node).then(function (pngDataUrl) {
var img = new Image();
img.onload = function () {
var context = canvas.getContext('2d');
context.translate(canvas.width, 0);
context.scale(-1, 1);
context.drawImage(img, 0, 0);
parent.removeChild(node);
parent.appendChild(canvas);
};
img.src = pngDataUrl;
});
And here is jsfiddle
Building on top of the Mozdev post that natevw references I've started a small project to render HTML to canvas in Firefox, Chrome & Safari. So for example you can simply do:
rasterizeHTML.drawHTML('<span class="color: green">This is HTML</span>'
+ '<img src="local_img.png"/>', canvas);
Source code and a more extensive example is here.
No such thing, sorry.
Though the spec states:
A future version of the 2D context API may provide a way to render fragments of documents, rendered using CSS, straight to the canvas.
Which may be as close as you'll get.
A lot of people want a ctx.drawArbitraryHTML/Element kind of deal but there's nothing built in like that.
The only exception is Mozilla's exclusive drawWindow, which draws a snapshot of the contents of a DOM window into the canvas. This feature is only available for code running with Chrome ("local only") privileges. It is not allowed in normal HTML pages. So you can use it for writing FireFox extensions like this one does but that's it.
You could spare yourself the transformations, you could use CSS3 Transitions to flip <div>'s and <ol>'s and any HTML tag you want. Here are some demos with source code explain to see and learn: http://www.webdesignerwall.com/trends/47-amazing-css3-animation-demos/
the next code can be used in 2 modes, mode 1 save the html code to a image, mode 2 save the html code to a canvas.
this code work with the library: https://github.com/tsayen/dom-to-image
*the "id_div" is the id of the element html that you want to transform.
**the "canvas_out" is the id of the div that will contain the canvas
so try this code.
:
function Guardardiv(id_div){
var mode = 2 // default 1 (save to image), mode 2 = save to canvas
console.log("Process start");
var node = document.getElementById(id_div);
// get the div that will contain the canvas
var canvas_out = document.getElementById('canvas_out');
var canvas = document.createElement('canvas');
canvas.width = node.scrollWidth;
canvas.height = node.scrollHeight;
domtoimage.toPng(node).then(function (pngDataUrl) {
var img = new Image();
img.onload = function () {
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0);
};
if (mode == 1){ // save to image
downloadURI(pngDataUrl, "salida.png");
}else if (mode == 2){ // save to canvas
img.src = pngDataUrl;
canvas_out.appendChild(img);
}
console.log("Process finish");
});
}
so, if you want to save to image just add this function:
function downloadURI(uri, name) {
var link = document.createElement("a");
link.download = name;
link.href = uri;
document.body.appendChild(link);
link.click();
}
Example of use:
<html>
<head>
</script src="/dom-to-image.js"></script>
</head>
<body>
<div id="container">
All content that want to transform
</div>
<button onclick="Guardardiv('container');">Convert<button>
<!-- if use mode 2 -->
<div id="canvas_out"></div>
</html>
Comment if that work.
Comenten si les sirvio :)
The easiest solution to animate the DOM elements is using CSS transitions/animations but I think you already know that and you try to use canvas to do stuff CSS doesn't let you to do. What about CSS custom filters? you can transform your elements in any imaginable way if you know how to write shaders. Some other link and don't forget to check the CSS filter lab.
Note: As you can probably imagine browser support is bad.
function convert() {
dom = document.getElementById('divname');
var script,
$this = this,
options = this.options,
runH2c = function(){
try {
var canvas = window.html2canvas([ document.getElementById('divname') ], {
onrendered: function( canvas ) {
window.open(canvas.toDataURL());
}
});
} catch( e ) {
$this.h2cDone = true;
log("Error in html2canvas: " + e.message);
}
};
if ( window.html2canvas === undefined && script === undefined ) {
} else {.
// html2canvas already loaded, just run it then
runH2c();
}
}

Categories

Resources