Dark/light mode with background scroll on change not behaving as intended - javascript

I have built a a small project combining dark/light mode with a background scroll on change.
The scrolling change works with a nice transition as light mode.
But when I toggle to dark mode it does change, but you have to scroll a little for it to change. I want it to change immediately without scrolling.
I tried a few different ways using some examples from codepen for the scroll and went with this approach.
scroll logic
window.sections = [...document.querySelectorAll(".section")];
document.body.style.background = window.sections[0].getAttribute("data-bg");
window.addEventListener("scroll", onScroll);
function onScroll() {
const section = window.sections
.map((section) => {
const el = section;
const rect = el.getBoundingClientRect();
return { el, rect };
})
.find((section) => section.rect.bottom >= window.innerHeight / 4);
document.body.style.background = section.el.getAttribute("data-bg");
}
Then I built the dark light mode where you toggle dark mode and it changes the data-attributes to all grey.
toggle logic
const toggle = document.getElementById("toggle");
const sections = document.getElementsByClassName("section");
toggleHandler = () => {
if (toggle.checked === false) {
console.log("false");
sections[0].setAttribute("data-bg", "#23b296");
sections[1].setAttribute("data-bg", "#f15f61");
sections[2].setAttribute("data-bg", "#DDBCF7");
sections[3].setAttribute("data-bg", "#fca971");
} if (toggle.checked === true)
{
console.log("true");
sections[0].setAttribute("data-bg", "grey");
sections[1].setAttribute("data-bg", "grey");
sections[2].setAttribute("data-bg", "grey");
sections[3].setAttribute("data-bg", "grey");
}
console.log([...sections]);
console.log(sections[0].dataset.bg);
};
toggle.addEventListener("click", toggleHandler);
Link to the repo
Link to deployed site
I'm a bit stuck really as to solve this issue. Any guidance would be appreciated.

Related

GSAP - ScrollJacking and Anchors not working properly

So far I've been working on this scroll-jacking section for this website and never have used GSAP before and have gotten myself just burned out and confused on what to do.
For starters when ever you scroll up or down on the page it just passes over the section and doesn't stop to do the scroll-jacking. It should be stopping and scrolling through section one, two, and three then continue down the rest of the page and then reverse when scrolling back up the page.
Another things I've dealt with is the menu on the left is href anchors and it should go to that section when you click on it.
I'm not expecting anybody to finish my work for me but just looking for guidance on what to do from here.
Here is the CodePen Link, any help is appreciated!
My code for quick reference:
I've been reading a lot of the documentation from GSAP about using the Observer and working from a lot of examples.
//handles scroll animations with gasp nd intersection observer.
window.addEventListener('load',(e)=>{
//register gasp with scrolltrigger
gsap.registerPlugin(ScrollTrigger);
//define scroll area
const scrollArea = document.getElementById("scrollArea");
//for mobile animation differences
const mobile = window.matchMedia('(max-width: 768px)')
//only for mobile
const nav = document.querySelector('.scrollContent')
///creates scroll trigger dependant on media query.
console.log(scrollArea.clientHeight - 340);
if (!mobile.matches){
gsap.to(scrollArea, {
//as user scrolls down on desktop the slides will scroll vertically
y: scrollArea.clientHeight * -1,
scrollTrigger: {
trigger: ".scrollArea",
pin:".scrollSection",
scrub: 18,
start: "-166px 50%",
end: "bottom 1100px",
}
});
}
//basic intersection observer options
let options = {
root: null,
rootMargin: '0px',
threshold: 0.5
}
//callback function for intersection observer.
function callback(entries) {
//entries are the boxes scrolling up
entries.forEach(entry => {
const intersecting = entry.isIntersecting
document.querySelector(".scrollSection").classList.add('opacity-animate')
if(entry.target.id !== 'services'){
//to add active class when step in view
const link = document.querySelector(`[href="#${entry.target.id}"]`);
//adds active class if in view and the link doesnt contain active
if(!link.classList.contains('active') && intersecting){
console.log(entry.target.id)
link.classList.add("active")
}
//removes active for backscrolling
else if(link.classList.contains('active') && !intersecting ){
link.classList.remove("active")
}
if(intersecting ){
if( entry.target.classList.contains('opacity-none') && !entry.target.classList.contains('opacity-animate') ){
entry.target.classList.remove('opacity-none')
entry.target.classList.add( 'opacity-animate' )
}
}else{
if(entry.target.classList.contains('opacity-animate') && !entry.target.classList.contains('opacity-none') ){
entry.target.classList.remove('opacity-animate')
entry.target.classList.add(entry.target.id !== 'one' && !entry.target.classList.contains('opacity-none') ? 'opacity-none' : '')
}else{
entry.target.classList.add(entry.target.id !== 'one' && !entry.target.classList.contains('opacity-none') ? 'opacity-none' : '')
}
}
}
})
}
//create the observer object
const observer = new IntersectionObserver(callback, options);
//observe boxes
let observerIds = ['section1', 'section2', 'section3'];
observerIds.forEach((id)=>{
observer.observe(document.getElementById(id))
})
})`

css dropdown not showing in iMac Safari browser

Total dropdown has been made on css and on scroll navigation position fixed made by a little bit vanilla javascript.
For Scroll Positions
/**
* Sticky JavaScript Navigations
* For Home Page Only
*/
window.onscroll = function () {
navSticky()
};
const mghFixedNav = document.getElementsByClassName("mgh-main-nav-area")[0];
// const sticky = llnav.offsetTop;
function navSticky() {
if (window.pageYOffset > 100) {
mghFixedNav.style.top = "50px";
mghFixedNav.style.width = "100%";
mghFixedNav.style.left = "0";
mghFixedNav.style.right = "0";
} else {
mghFixedNav.style.top = "75px";
mghFixedNav.style.width = "95%";
mghFixedNav.style.left = "2.5%";
mghFixedNav.style.right = "2.5%";
}
}
Firefox and Chrome browser work perfectly but the Safari browser in iMac not showing the navbar and dropdown.
Here is the codepen link
Anybody can help me ? Thanks in advance..
Just remove the following CSS code and it will start working on Safari:
//header top
header {
overflow: hidden;
}
Here is the codepen link by which you can directly verify that.
You should not really use window.onscroll for creating such effects, there are better, more performant ways to do this, the intersection observer.
With this you can check how much of an element is visible without listening to the scroll event and without recalculating it permanently, the browser will do this for you.
First you define the observer:
var options = {
rootMargin: '10px',
threshold: 0.2
}
var observer = new IntersectionObserver(callback, options);
in your case your target would be something like this:
var target = document.getElementsByClassName("mgh-main-nav-area")[0];
observer.observe(target);
I can't write the code for your problem from the top of my head, I'm sorry about that. But I hope I could at least point you in the right direction on which tool you should use.
Here is one example I found for toggling a sticky-class for a fixed nav :)

Prevent vertical scroll on swipe -vanilla JS

I wrote this code to add swipe function for an image slider. The slider is working correctly.
However when i perform a right or left swipe there is some vertical scrolling which is distracting and annoying.
I'm storing the reference to touchstart in the touch object.
And on touchend event, if vertical distance (lenY) is more than 50, i trigger preventDefault on the touchstart.
This isn't working.
Simplest option is to call preventDefault directly on touchStart. But the image slider occupies a large part of the mobile screen making scrolling down the page tricky.
I need to pass the lenY (vertical distance) to the touch start handler to prevent default action.
function triggerTouch() {
"use strict";
var tZone = document.getElementById('sl-m'),
touch = {},
startX = 0,
startY = 0,
endX = 0,
endY = 0;
if (tZone) {
tZone.addEventListener('touchstart', function (e) {
startX = e.changedTouches[0].screenX;
startY = e.changedTouches[0].screenY;
// store reference to touch event
touch.start = e;
}, false);
tZone.addEventListener('touchend', function (e) {
endX = e.changedTouches[0].screenX;
endY = e.changedTouches[0].screenY;
var lenX = Math.abs(endX - startX);
var lenY = Math.abs(endY - startY);
// check if user intended to scroll down
if (lenY < 50 && lenX > 50) {
touch.start.preventDefault();
e.preventDefault();
swipe(tZone, startX, endX);
}
}, false);
}
}
Since i haven't got an answer i am posting my own answer, hoping someone can provide the correct implementation.
I ended up using the css overflow property to temporarily disable vertical scroll.
This works perfectly though there is a small side effect. Once you swipe through the image slider, the scroll is disabled.
A swipe upwards is required to restore scroll to the page. Its not noticeable but i still want to figure the right way.
var touch = {};
window.onload = function () {
"use strict";
document.body.addEventListener("touchstart", touchHandler);
document.body.addEventListener("touchend", touchHandler);
};
function touchHandler(e) {
"use strict";
var el = e.target;
if (el.parentNode.id === "sl-m") {
if (e.type === "touchstart") {
touch.startX = e.changedTouches[0].screenX;
touch.startY = e.changedTouches[0].screenY;
} else {
touch.endX = e.changedTouches[0].screenX;
touch.endY = e.changedTouches[0].screenY;
touch.lenX = Math.abs(touch.endX - touch.startX);
touch.lenY = Math.abs(touch.endY - touch.startY);
if (touch.lenY < 20) {
// disable scroll
document.body.style.overflowY = "hidden";
// do swipe related stuff
swipe(el.parentNode);
} else {
// enable scroll if swipe was not intended
document.body.style.overflowY = "scroll";
}
}
} else {
// keep scroll enabled if touch is outside the image slider
document.body.style.overflowY = "scroll";
}
}
I want to share the solution that works for me. The above solution did not work on ios. I am sorry for my English. I do not know english.
function stop(e){
e=e || event;
e.preventDefault;
}
window.onscroll=stop(); //-->Yes, we will use it ..
For example, where you will use;
function move(event){
var finish=event.touches[0].clientX;
var verticalFinish=event.touches[0].clientY;
var diff=finish-strt;
var verticalDiff=verticalStrt-verticalFinish;
var f;
if(diff<0 && (Math.abs(diff)>Math.abs(verticalDiff)/3)){
f=verticalDiff+widthOffset;
slayt[x].style.left=diff+"px";
slayt[x].style.transition="none";
slayt[y].style.left=f+"px";
slayt[y].style.transition="none";
window.onscroll=stop(); //-->we used it here :)
}
else if(diff>0 && (Math.abs(diff)>Math.abs(verticalDiff)/3)){
f=diff-widthOffset;
slayt[x].style.left=diff+"px";
slayt[x].style.transition="none";
slayt[z].style.left=f+"px";
slayt[z].style.transition="none";
window.onscroll=stop();//-->we used it here :)
}
}
but there is a small problem. cancels if there is another function related to scrolling. return true; it does not work. I also write twice if I have a function related to the slider inside and outside the touchend.
function end(event){
//"touchend" related codes...
//bla bla
window.onscroll=function(){m=window.pageYOffset;console.log(m);if(m>=850)
{buton.style.display="block";}else{buton.style.display="none";}}
}
If it is useful, I will be happy...
Update :
I typed wrong. I want to fix. Actually, the scroll event cannot be canceled unfortunately. So the event we canceled above, scroll is not a vertical scroll event. All events.
window.onscroll=stop(); // ==>improper use
stop(); // ==> actually - Correct usage
It just needs to be written so stop().
html,
body {
overflow: hidden;
}
Did you try this?

How to implement canvas panning with Fabric.js

I have a Fabric.js canvas and I want to implement the full-canvas panning that software packages usually do with a "hand" tool. It's when you press one of the mouse buttons, then move over the canvas while holding the mouse button and the visible portion of the canvas changes accordingly.
You can see in this video what I want to achieve.
In order to implement this functionality I wrote the following code:
$(canvas.wrapperEl).on('mousemove', function(evt) {
if (evt.button == 2) { // 2 is the right mouse button
canvas.absolutePan({
x: evt.clientX,
y: evt.clientY
});
}
});
But it doesn't work. You can see in this video what happens.
How can I modify my code in order:
For panning to work like in the first video?
For the event handler to consume the event? It should prevent the context menu from appearing when the user presses or releases the right mouse button.
An easy way to pan a Fabric canvas in response to mouse movement is to calculate the cursor displacement between mouse events and pass it to relativePan.
Observe how we can use the screenX and screenY properties of the previous mouse event to calculate the relative position of the current mouse event:
function startPan(event) {
if (event.button != 2) {
return;
}
var x0 = event.screenX,
y0 = event.screenY;
function continuePan(event) {
var x = event.screenX,
y = event.screenY;
fc.relativePan({ x: x - x0, y: y - y0 });
x0 = x;
y0 = y;
}
function stopPan(event) {
$(window).off('mousemove', continuePan);
$(window).off('mouseup', stopPan);
};
$(window).mousemove(continuePan);
$(window).mouseup(stopPan);
$(window).contextmenu(cancelMenu);
};
function cancelMenu() {
$(window).off('contextmenu', cancelMenu);
return false;
}
$(canvasWrapper).mousedown(startPan);
We start panning on mousedown and continue panning on mousemove. On mouseup, we cancel the panning; we also cancel the mouseup-cancelling function itself.
The right-click menu, also known as the context menu, is cancelled by returning false. The menu-cancelling function also cancels itself. Thus, the context menu will work if you subsequently click outside the canvas wrapper.
Here is a page demonstrating this approach:
http://michaellaszlo.com/so/fabric-pan/
You will see three images on a Fabric canvas (it may take a moment or two for the images to load). You'll be able to use the standard Fabric functionality. You can left-click on the images to move them around, stretch them, and rotate them. But when you right-click within the canvas container, you pan the whole Fabric canvas with the mouse.
I have an example on Github using fabric.js Canvas panning: https://sabatinomasala.github.io/fabric-clipping-demo/
The code responsible for the panning behaviour is the following: https://github.com/SabatinoMasala/fabric-clipping-demo/blob/master/src/classes/Panning.js
It's a simple extension on the fabric.Canvas.prototype, which enables you to toggle 'drag mode' on the canvas as follows:
canvas.toggleDragMode(true); // Start panning
canvas.toggleDragMode(false); // Stop panning
Take a look at the following snippet, documentation is available throughout the code.
const STATE_IDLE = 'idle';
const STATE_PANNING = 'panning';
fabric.Canvas.prototype.toggleDragMode = function(dragMode) {
// Remember the previous X and Y coordinates for delta calculations
let lastClientX;
let lastClientY;
// Keep track of the state
let state = STATE_IDLE;
// We're entering dragmode
if (dragMode) {
// Discard any active object
this.discardActiveObject();
// Set the cursor to 'move'
this.defaultCursor = 'move';
// Loop over all objects and disable events / selectable. We remember its value in a temp variable stored on each object
this.forEachObject(function(object) {
object.prevEvented = object.evented;
object.prevSelectable = object.selectable;
object.evented = false;
object.selectable = false;
});
// Remove selection ability on the canvas
this.selection = false;
// When MouseUp fires, we set the state to idle
this.on('mouse:up', function(e) {
state = STATE_IDLE;
});
// When MouseDown fires, we set the state to panning
this.on('mouse:down', (e) => {
state = STATE_PANNING;
lastClientX = e.e.clientX;
lastClientY = e.e.clientY;
});
// When the mouse moves, and we're panning (mouse down), we continue
this.on('mouse:move', (e) => {
if (state === STATE_PANNING && e && e.e) {
// let delta = new fabric.Point(e.e.movementX, e.e.movementY); // No Safari support for movementX and movementY
// For cross-browser compatibility, I had to manually keep track of the delta
// Calculate deltas
let deltaX = 0;
let deltaY = 0;
if (lastClientX) {
deltaX = e.e.clientX - lastClientX;
}
if (lastClientY) {
deltaY = e.e.clientY - lastClientY;
}
// Update the last X and Y values
lastClientX = e.e.clientX;
lastClientY = e.e.clientY;
let delta = new fabric.Point(deltaX, deltaY);
this.relativePan(delta);
this.trigger('moved');
}
});
} else {
// When we exit dragmode, we restore the previous values on all objects
this.forEachObject(function(object) {
object.evented = (object.prevEvented !== undefined) ? object.prevEvented : object.evented;
object.selectable = (object.prevSelectable !== undefined) ? object.prevSelectable : object.selectable;
});
// Reset the cursor
this.defaultCursor = 'default';
// Remove the event listeners
this.off('mouse:up');
this.off('mouse:down');
this.off('mouse:move');
// Restore selection ability on the canvas
this.selection = true;
}
};
// Create the canvas
let canvas = new fabric.Canvas('fabric')
canvas.backgroundColor = '#f1f1f1';
// Add a couple of rects
let rect = new fabric.Rect({
width: 100,
height: 100,
fill: '#f00'
});
canvas.add(rect)
rect = new fabric.Rect({
width: 200,
height: 200,
top: 200,
left: 200,
fill: '#f00'
});
canvas.add(rect)
// Handle dragmode change
let dragMode = false;
$('#dragmode').change(_ => {
dragMode = !dragMode;
canvas.toggleDragMode(dragMode);
});
<div>
<label for="dragmode">
Enable panning
<input type="checkbox" id="dragmode" name="dragmode" />
</label>
</div>
<canvas width="300" height="300" id="fabric"></canvas>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.15/fabric.min.js"></script>
Not sure about FabricJS, but it could be in such a way:
for it to work like in the first video:
By making use of CSS cursor property, toggling it on mousedown and mouseup events using javascript.
the event handler consume the event (prevent the context menu from appearing, when the user releases the right mouse button):
Using javascript we return false on contextmenu event
CODE: with a little problem ( * )
using jQuery JS Fiddle 1
$('#test').on('mousedown', function(e){
if (e.button == 2) {
// if right-click, set cursor shape to grabbing
$(this).css({'cursor':'grabbing'});
}
}).on('mouseup', function(){
// set cursor shape to default
$(this).css({'cursor':'default'});
}).on('contextmenu', function(){
//disable context menu on right click
return false;
});
Using raw javascript JS Fiddle 2
var test = document.getElementById('test');
test.addEventListener('mousedown', function(e){
if (e.button == 2) {
// if right-click, set cursor shape to grabbing
this.style.cursor = 'grabbing';
}
});
test.addEventListener('mouseup', function(){
// set cursor shape to default
this.style.cursor = 'default';
});
test.oncontextmenu = function(){
//disable context menu on right click
return false;
}
( * ) Problem:
The above snippets works as it should but there's a cross-browser issue, if you check the above fiddles in Firefox - or Opera -you'll see the correct behavior, when checked in Chrome and IE11 - didn't checked it with Edge or Safari - I found that Chrome and IE don't support the grabbing cursor shape, so in the above code snippets, change the cursor lines into this:
jQuery: $(this).css({'cursor':'move'}); JS Fiddle 3
Raw javascript: this.style.cursor = 'move'; JS Fiddle 4
Now we have a working code but without the hand cursor. but there is the following solution:-
SOLUTIONS:
Chrome and Safari support grab and grabbing with the -webkit- prefix like:
$(this).css({'cursor': '-webkit-grabbing'});
but you need to make browser sniffing first, if Firefox then the default and standard code, if Chrome and Safari then with the -webkit- prefix, and this still makes IE out of the game.
Have a look at this example, test it with Chrome, Safari, Firefox, Opera and IE you can see that cursor: url(foo.bar) works and supported in ALL browsers.
Chrome, Safari, Firefox and Opera shows the yellow smile image smiley.gif, but IE shows the red ball cursor url(myBall.cur).
So I think you can make use of this, and a grabbing hand image like this
Or this:
You can use an image like the above, a png or gif format with all browsers except IE which supports .cur, so you need to find a way to convert it into a .cur. Google search shows many result of convert image to cur
Note that, although this cursor:url(smiley.gif),url(myBall.cur),auto; - with fallback support separated by comma, works well in the W3Schools example shown above, I couldn't get it to work the same way in javascript, I tried $(this).css({'cursor': 'grabbing, move'}); but it didn't work.
I also tried doing it as CSS class
.myCursor{ cursor: grabbing, -webkit-grabbing, move; }
Then with jQuery $(this).addClass('myCursor'); but no avail either.
So you still need to make browser sniffing whether you are going the second solution or a hybrid fix of the both solutions, this is my code which I've used couple times to detect browser and it worked well at the time of this post but you porbabely won't need the "Mobile" and "Kindle" parts.
// Detecting browsers
$UA = navigator.userAgent;
if ($UA.match(/firefox/i)) {
$browser = 'Firefox';
} else if ($UA.indexOf('Trident') != -1 && $UA.indexOf('MSIE') == -1) {
$browser = 'MSIE';
} else if ($UA.indexOf('MSIE') != -1) {
$browser = 'MSIE';
} else if ($UA.indexOf('OPR/') != -1) {
$browser = 'Opera';
} else if ($UA.indexOf("Chrome") != -1) {
$browser = 'Chrome';
} else if ($UA.indexOf("Safari")!=-1) {
$browser = 'Safari';
}
if($UA.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Nokia|Mobile|Opera Mini/i)) {
$browser = 'Mobile';
}else if($UA.match(/KFAPWI/i)){
$browser = 'Kindle';
}
console.log($browser);
Resources:
https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
http://www.w3schools.com/cssref/pr_class_cursor.asp
https://css-tricks.com/almanac/properties/c/cursor/
Google images search of grabbing hand cursor
i have made an example on jsfiddle , that we can actually drag the whole canvas with all its objects, into a parent div, like the picture,and i will try to explain it step by step.
First of all i download the drag library jquery.dradscroll.js, you can find it on the net. This is a small js file that with little changes it can helps us complete the task.
download link: http://www.java2s.com/Open-Source/Javascript_Free_Code/jQuery_Scroll/Download_jquery_dragscroll_Free_Java_Code.htm
create the container that holds our canvas.
<div class="content">
<canvas id="c" width="600" height="700" ></canvas>
</div>
little css
.content{
overflow:auto;
width:400px;
height:400px;
}
javascript:
a. create the canvas.
b. make default cursor , when it is over canvas , open hand
canvas.defaultCursor = 'url(" http://maps.gstatic.com/intl/en_us/mapfiles/openhand_8_8.cur") 15 15, crosshair';
c. override the __onMouseDown function , for change to the closedhand cursor( at end).
fabric.Canvas.prototype.__onMouseDown = function(e){
// accept only left clicks
var isLeftClick = 'which' in e ? e.which === 1 : e.button === 1;
if (!isLeftClick && !fabric.isTouchSupported) {
return;
}
if (this.isDrawingMode) {
this._onMouseDownInDrawingMode(e);
return;
}
// ignore if some object is being transformed at this moment
if (this._currentTransform) {
return;
}
var target = this.findTarget(e),
pointer = this.getPointer(e, true);
// save pointer for check in __onMouseUp event
this._previousPointer = pointer;
var shouldRender = this._shouldRender(target, pointer),
shouldGroup = this._shouldGroup(e, target);
if (this._shouldClearSelection(e, target)) {
this._clearSelection(e, target, pointer);
}
else if (shouldGroup) {
this._handleGrouping(e, target);
target = this.getActiveGroup();
}
if (target && target.selectable && !shouldGroup) {
this._beforeTransform(e, target);
this._setupCurrentTransform(e, target);
}
// we must renderAll so that active image is placed on the top canvas
shouldRender && this.renderAll();
this.fire('mouse:down', { target: target, e: e });
target && target.fire('mousedown', { e: e });
if(!canvas.getActiveObject() || !canvas.getActiveGroup()){
flag=true;
//change cursor to closedhand.cur
canvas.defaultCursor = 'url("http://maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur") 15 15, crosshair';
}//end if
override the __onMouseUp event ,to change back the cursor to openhand.
fabric.Canvas.prototype.__onMouseUp = function(e){
if(flag){
canvas.defaultCursor = 'url(" http://maps.gstatic.com/intl/en_us/mapfiles/openhand_8_8.cur") 15 15, crosshair';
flag=false;
}
};
You initialize the dragScroll() to work on the content that hosts the canvas:
$('.content').dragScroll({});
Some small changes on the jquery.dragScroll.js file, so as to understand when to drag the canvas and when not. On mousedown() event we add an if statement to check whether we have an active object or group.If true ,no canvas drag.
$($scrollArea).mousedown(function (e) {
if (canvas.getActiveObject() || canvas.getActiveGroup()) {
console.log('no drag');return;
} else {
console.log($('body'));
if (typeof options.limitTo == "object") {
for (var i = 0; i < options.limitTo.length; i++) {
if ($(e.target).hasClass(options.limitTo[i])) {
doMousedown(e);
}
}
} else {
doMousedown(e);
}
}
});
on mousedown event we grab the DOM element (.content) and get the top & left position
function doMousedown(e) {
e.preventDefault();
down = true;
x = e.pageX;
y = e.pageY;
top = e.target.parentElement.parentElement.scrollTop; // .content
left = e.target.parentElement.parentElement.scrollLeft;// .content
}
If we dont want to have the scrollbars visible:
.content{
overflow:hidden;
width:400px;
height:400px;
}
There is a small problem though,jsfiddle, accepts only https libraries ,so it blocks fabricjs, except if you add it from 'https://rawgit.com/kangax/fabric.js/master/dist/fabric.js', but again it still blocks it some times (at least on my chrome and mozilla).
jsfiddle example : https://jsfiddle.net/tornado1979/up48rxLs/
you may have better luck ,than me, on your browser , but it would definitelly work on your live app.
Anyway,
i hope helps ,good luck.
I know this has already been answered, but I redid the pen created in this answer using the new version of fabricjs (4.5.0)
Pen: https://codepen.io/201flaviosilva/pen/GROLbQa
In this case, I used the middle button in the mouse to pan :)
// Enable mouse middle button
canvas.fireMiddleClick = true;
// Mouse Up Event
if (e.button === 2) currentState = STATE_IDLE;
// Mouse Down Event
if (e.button === 2) currentState = STATE_PANNING;
:)

How to change pop-up location of google charts tooltip

I currently have html enabled tooltips that also display "sub graphs". However, it would be nice if it was possible to have all tooltips pop up in a fixed location or have an offset that adjusted their relative poition.
This is an example of the kind of tooltip that I have (blank data). I'd like to move it to the right. Any suggestions would be appreciated, including any javascript trickery.
whilst the answer is very good it is a little outdated now. Google has implemented CSS control so there is greater flexibility without the need to hack the JavaScript.
.google-visualization-tooltip { position:relative !important; top:0 !important;right:0 !important; z-index:+1;}
will provide a tooltip fixed at the bottom of the chart, live example: http://www.taxformcalculator.com/federal-budget/130000.html
alternatively you could just tweak the left margin...
.google-visualization-tooltip { margin-left: 150px !important; z-index:+1;}
Note that pulling the container forward with z-index reduces (but does not stop entirely) visibility flicker as the mouse moves. The degree of flicker will vary on chart size, call etc. Personally, I prefer to fix the tool tip and make it part of the design as per the first example. Hope this helps those who are deterred by the JS hack (which is good but really no longer necessary).
The tooltip position is set inline, so you need to listen for DOM insertion of the tooltip and change the position manually. Mutation events are deprecated, so use a MutationObserver if it is available (Chrome, Firefox, IE11) and a DOMNodeInserted event handler if not (IE 9, 10). This will not work in IE8.
google.visualization.events.addOneTimeListener(myChart, 'ready', function () {
var container = document.querySelector('#myChartDiv > div:last-child');
function setPosition () {
var tooltip = container.querySelector('div.google-visualization-tooltip');
tooltip.style.top = 0;
tooltip.style.left = 0;
}
if (typeof MutationObserver === 'function') {
var observer = new MutationObserver(function (m) {
for (var i = 0; i < m.length; i++) {
if (m[i].addedNodes.length) {
setPosition();
break; // once we find the added node, we shouldn't need to look any further
}
}
});
observer.observe(container, {
childList: true
});
}
else if (document.addEventListener) {
container.addEventListener('DOMNodeInserted', setPosition);
}
else {
container.attachEvent('onDOMNodeInserted', setPosition);
}
});
The MutationObserver should be fine, but the events may need some work; I didn't test them.
I had more or less the same question as Redshift, having been trying to move the tooltip relative to the node being hovered over. Using asgallant's fantastic answer I've implemented his code as below.
I haven't been able to test whether this works with the MutationObserver because during my testing in Firefox, Chrome and IE11 it always fails that test and uses addEventListener. The docs suggest it should work though.
I had to introduce a timeout to actually manipulate the styles as otherwise the left and top position of the element was always reported as 0. My assumption is that the event fired upon addition of the node but the DOM wasn't quite ready. This is just a guess though and I'm not 100% happy with implementing it in this way.
var chart = new google.visualization.LineChart(document.getElementById('line_chart'));
google.visualization.events.addOneTimeListener(chart, 'ready', function () {
var container = document.querySelector('#line_chart > div:last-child');
function setPosition(e) {
if (e && e.target) {
var tooltip = $(e.target);
setTimeout(function () {
var left = parseFloat(tooltip.css('left')) - 49;
var top = parseFloat(tooltip.css('top')) - 40;
tooltip.css('left', left + 'px');
tooltip.css('top', top + 'px');
$(".google-visualization-tooltip").fadeIn(200);
}, 1);
}
else {
var tooltip = container.querySelector('.google-visualization-tooltip');
var left = parseFloat(tooltip.style.left) - 49;
var top = parseFloat(tooltip.style.top) - 40;
tooltip.style.left = left + 'px';
tooltip.style.top = top + 'px';
$(".google-visualization-tooltip").fadeIn(200);
}
}
if (typeof MutationObserver === 'function') {
var observer = new MutationObserver(function (m) {
if (m.length && m[0].addedNodes.length) {
setPosition(m);
}
});
observer.observe(container, {
childList: true
});
}
else if (document.addEventListener) {
container.addEventListener('DOMNodeInserted', setPosition);
}
else {
container.attachEvent('onDOMNodeInserted', setPosition);
}
});
chart.draw(data, options);
}
EDIT: Updated to get the MutationObserver working following asgallant's comment.

Categories