I'm trying to create a simple Gantt chart in a tab that is based from this tutorial - https://webdesign.tutsplus.com/tutorials/build-a-simple-gantt-chart-with-css-and-javascript--cms-33813.
I am able to create the timeline in the tabs but the offsetLeft and offsetWidth is not working properly - https://gyazo.com/2e43741d106ecd4eac21d72aa520368a.
I wanted to get the offsetLeft and offsetWidth of each li elements but sometimes it returns a value of zero.
Here is the javascript code:
<script type="text/javascript">
jQuery(function($){
//create chart function
function createChart_tab(e) {
const years_tab = document.querySelectorAll('.research-proj-tabs .chart-years li');
const tasks_tab = document.querySelectorAll('.research-proj-tabs .chart-bars li');
const yearsArray_tab = [...years_tab];
tasks_tab.forEach(el => {
//1
const duration = el.dataset.duration.split("-");
//2
const startYear = duration[0];
const endYear = duration[1];
let left = 0,
width = 0;
//3
if (startYear) {
const filteredArray = yearsArray_tab.filter(year => year.textContent == startYear);
left = filteredArray[0].offsetLeft;
}
// 4
if (endYear) {
const filteredArray = yearsArray_tab.filter(year => year.textContent == endYear);
width = filteredArray[0].offsetLeft + filteredArray[0].offsetWidth - left;
}
// 1
el.style.left = `${left}px`;
el.style.width = `${width}px`;
// 4
if (e.type == "load") {
// 2
el.style.backgroundColor = el.dataset.color;
// 3
el.style.opacity = 1;
}
});
}
//window.addEventListener("onload", createChart);
window.addEventListener("load", createChart_tab);
window.addEventListener("resize", createChart_tab);
});
</script>
While HTML and CSS is following the same layout here: https://webdesign.tutsplus.com/tutorials/build-a-simple-gantt-chart-with-css-and-javascript--cms-33813
I just want to figure out what am I missing here. Thanks!
Related
i'm desining a horizzontal scrolling website, but i want that feature only in Desktop mode and i would like to let standard scrolling in tablets and mobile devices.
right now this is my setup:
<script>
document.addEventListener('touchstart', handler, {passive: true});
addEventListener(document, "touchstart", function(e) {
console.log(e.defaultPrevented); // will be false
e.preventDefault(); // does nothing since the listener is passive
console.log(e.defaultPrevented); // still false
});
const SCROLL_SPEED = 70;
requestAnimationFrame(function scroll() {
const nextScrollX = sectionAnchorPointer[nextSectionIndex];
// linear animtion
// if (Math.abs(window.scrollX - nextScrollX) > SCROLL_SPEED) {
// const val =
// -Math.abs(window.scrollX - nextScrollX) / (window.scrollX - nextScrollX);
// window.scroll(window.scrollX + val * SCROLL_SPEED, window.scrollY);
// } else {
// window.scroll(nextScrollX, window.scrollY);
// }
// curve animation
if (Math.abs(window.scrollX - nextScrollX) > 1) {
let val = (nextScrollX - window.scrollX) / 8;
val = val > 0 ? Math.max(val, 1) : Math.min(val, -1);
window.scroll(window.scrollX + val, window.scrollY);
} else {
window.scroll(nextScrollX, window.scrollY);
}
requestAnimationFrame(scroll);
});
let sectionAnchorPointer = [];
const resizeHandler = () => {
const content1 = document.getElementById("content1");
const content2 = document.getElementById("content2");
const content3 = document.getElementById("content3");
const content4 = document.getElementById("content4");
sectionAnchorPointer = [
content1.offsetLeft,
content2.offsetLeft,
content3.offsetLeft,
content4.offsetLeft
];
};
window.addEventListener("resize", resizeHandler);
let nextSectionIndex = 0;
const getCurrentSectionIndex = () =>
sectionAnchorPointer.findIndex((leftValue, i, array) => {
const scrollX = Math.ceil(window.scrollX); // Fixed a bug where scrollX was decimalized
const rightValue = array[i + 1] ?? Infinity;
return leftValue <= scrollX && scrollX < rightValue;
});
window.addEventListener("wheel", ({ deltaY }) => {
const currentSectionIndex = getCurrentSectionIndex();
const add = Math.abs(deltaY) / deltaY;
nextSectionIndex = currentSectionIndex + add;
nextSectionIndex = Math.min(
sectionAnchorPointer.length - 1,
Math.max(0, nextSectionIndex)
);
});
resizeHandler();
nextSectionIndex = getCurrentSectionIndex();
</script>
#media and screen (min-width: 1050px){
}
body {
overflow-y: hidden;
}
section {
min-width: 100vw!important;
min-height: 100vh!important;
}
}
}
I pasted the first lines of the JS code from documentation about passive Event listeners (official documentation) but i cannot understand how to apply that to my code in order to allow the scrolling in narrow screens.
right now the console output is the following:
Warning: Uncaught ReferenceError: handler is not defined
[Violation] Added non-passive event listener to a scroll-blocking 'touchstart' event. Consider marking event handler as 'passive' to make the page more responsive. See https://www.chromestatus.com/feature/5745543795965952
link to the site: site
thank you in advance for your time.
My fabric application requires a feature that allows the user edit textboxes within groups. I almost have it working. As it stands, this is how my application works:
Click on a group
Checks to see if you are clicking on a textbox. If yes...
Ungroups the group
Sets the focus on the textbox so that the user can make edits.
On "editing:exited," re-groups the objects
Here is a fiddle with my code:
https://jsfiddle.net/1dr8jn26/4/
Do the following to replicate my issue:
Add 2 input boxes using the button at the top
Group the input boxes using the button at the top
Click once on one of the input boxes in the group you created
Click on the canvas outside of the group
Click back on the same input box
Click on the canvas outside of the group again
Repeat 5 & 6
You will notice that the group creates duplicates of itself off to the side of the page. What I have discovered on my own:
"object.on('editing:exited... is what keeps getting run over and over.
The "duplication counter" resets if you click on one input box vs another.
When the duplicates appear, if you click on some of them, they disappear.
I am still relatively new to programming, so i'm sure this is something obvious to the experienced coders here. Any help would be appreciated!
canvas.on('mouse:down', function(options) {
if (options.target) {
var thisTarget = options.target;
var mousePos = canvas.getPointer(options.e);
if (thisTarget.isType('group')) {
var groupPos = {
x: thisTarget.left,
y: thisTarget.top
}
var currentGroup = [];
var groupItems = []
groupItems = thisTarget._objects;
thisTarget.forEachObject(function(object,i) {
currentGroup[i] = object;
currentGroup.push(object);
})
thisTarget.forEachObject(function(object,i) {
if(object.type == "textbox"){
console.log("Start for statement that finds the x and y for each
object")
var matrix = thisTarget.calcTransformMatrix()
var newPoint = fabric.util.transformPoint({y: object.top, x:
object.left}, matrix)
var objectPos = {
xStart: newPoint.x,
xEnd: newPoint.x + object.width,
yStart: newPoint.y,
yEnd: newPoint.y + object.height
}
if (mousePos.x >= objectPos.xStart && mousePos.x <=
(objectPos.xEnd)) {
if (mousePos.y >= objectPos.yStart && mousePos.y <=
objectPos.yEnd) {
function ungroup (group) {
groupItems = group._objects;
group._restoreObjectsState();
canvas.remove(group);
for (var i = 0; i < groupItems.length; i++) {
canvas.add(groupItems[i]);
}
canvas.renderAll();
};
ungroup(thisTarget)
canvas.setActiveObject(object);
object.enterEditing();
object.selectAll();
object.on('editing:exited', function (options) {
var items = [];
groupItems.forEach(function (obj) {
items.push(obj);
canvas.remove(obj);
});
console.log(JSON.stringify(groupItems))
var grp = new fabric.Group(items, {});
canvas.add(grp);
});
}
}
}
});
}
}
});
-Chris
Have been looking at the code and I think I found a solution. The group is added two times in the group groupItems. Once at the start and a second time when ungrouping the items.
Used the code below in your fiddle and that works...
canvas.on('mouse:down', function(options) {
var groupItems;
if (options.target) {
var thisTarget = options.target;
var mousePos = canvas.getPointer(options.e);
var editTextbox = false;
var editObject;
if (thisTarget.isType('group')) {
var groupPos = {
x: thisTarget.left,
y: thisTarget.top
}
thisTarget.forEachObject(function(object,i) {
if(object.type == "textbox"){
var matrix = thisTarget.calcTransformMatrix();
var newPoint = fabric.util.transformPoint({y: object.top, x: object.left}, matrix);
var objectPos = {
xStart: newPoint.x - (object.width * object.scaleX) / 2,//When OriginX and OriginY are centered, otherwise xStart: newpoint.x - object.width * object.scaleX etc...
xEnd: newPoint.x + (object.width * object.scaleX) / 2,
yStart: newPoint.y - (object.height * object.scaleY) / 2,
yEnd: newPoint.y + (object.height * object.scaleY) / 2
}
if ((mousePos.x >= objectPos.xStart && mousePos.x <= objectPos.xEnd) && (mousePos.y >= objectPos.yStart && mousePos.y <= objectPos.yEnd)) {
function ungroup (group) {
groupItems = group._objects;
group._restoreObjectsState();
canvas.remove(group);
for (var i = 0; i < groupItems.length; i++) {
if(groupItems[i] != "textbox"){
groupItems[i].selectable = false;
}
canvas.add(groupItems[i]);
}
canvas.renderAll();
};
ungroup(thisTarget);
canvas.setActiveObject(object);
object.enterEditing();
object.selectAll();
editObject = object;
var exitEditing = true;
editObject.on('editing:exited', function (options) {
if(exitEditing){
var items = [];
groupItems.forEach(function (obj) {
items.push(obj);
canvas.remove(obj);
});
var grp
grp = new fabric.Group(items, {});
canvas.add(grp);
exitEditing = false;
}
});
}
}
});
}
}
});
Here is the fiddle: https://jsfiddle.net/u3Lfnja2/1/
I am trying to work out a way to detect wordwrap in a specific span tag inside a banner. If it wraps to 2 lines then increase the overall height of the container by 56px. There is also a sub headline (headline2) which also needs to increase (or decrease) the height by 40px.
I have written some basic JS code here which checks the div height of the span but its not great & also will only work for 3 lines.
// Variable banner heights
var hl11sub = 368;
var hl21sub = 448;
var hl31sub = 548;
var hl12sub = 416;
var hl22sub = 496;
var hl32sub = 576;
var hLFontSizeCSS = window.getComputedStyle(headlineText, null).getPropertyValue("font-size");
var hL2FontSizeCSS = window.getComputedStyle(headline2text, null).getPropertyValue("font-size");
var bannerHeightCSS = window.getComputedStyle(banner, null).getPropertyValue("height");
var headlineHeight = headlineText.offsetHeight;
var hL2HeadHeight = headline2text.offsetHeight;
var headHeight = headlineText.style.lineHeight = parseInt(hLFontSizeCSS) + 10 + "px";
var hL2Height = headline2text.style.lineHeight = parseInt(hL2FontSizeCSS) + 10 + "px";
// Text Height values
var hL1LineHeight = parseInt(headHeight); // 8 is line height & padding
var hL2LinesHeight = 140;
var hL3LinesHeight = 195;
// HL2 height values
var hL2TextOver1LineHeight = parseInt(hL2Height); // 8 is line height & padding
var hL2TextOver2LineHeight = 84;
if(hL2HeadHeight == hL2TextOver1LineHeight && headlineHeight == hL1LineHeight){
banner.style.height = hl11sub + "px";
}
else if(hL2HeadHeight == hL2TextOver1LineHeight && headlineHeight == hL2LinesHeight){
banner.style.height = hl21sub + "px";
}
else if(hL2HeadHeight == hL2TextOver1LineHeight && headlineHeight >= hL3LinesHeight){
banner.style.height = hl31sub + "px";
}
else if(hL2HeadHeight == hL2TextOver2LineHeight && headlineHeight == hL1LineHeight){
// Single headline with 2 lines sub
banner.style.height = hl12sub + "px";
}
else if(hL2HeadHeight == hL2TextOver2LineHeight && headlineHeight == hL2LinesHeight){
// 2 headlines with 2 lines sub
banner.style.height = hl22sub + "px";
}
else {
banner.style.height = hl32sub + "px";
// 3 headlines with 2 lines sub
It needs to only change the height of the banner depending on if the span words wrap once, twice, three times etc.
Any suggestions or help with this would be greatly appreciated.
Here is a very basic implementation on how to detect when a line is wrapped hopefully this gives you a good idea where to start and integrate it into your app.
Heres the docs for stuff used
https://developer.mozilla.org/en/docs/Web/API/EventTarget/addEventListener
https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle
https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
You mentioned the height changing and you needing to know when its wrapped you can use a mutation observer to check when the style has changed then check if its wrapped.
Resize the demo window to see results
any questions i'll try get to them asap if i've misunderstood i'll happily change :)
const h1 = document.querySelector('h1');
const banner = document.querySelector('.banner');
//handles style changes on banner to check wrapping
const observer = new MutationObserver(mutations =>
mutations.forEach(mutationRecord => onLineWrapDoSomething())
);
observer.observe(banner, { attributes : true, attributeFilter : ['style'] });
// handles window resize events
window.addEventListener('resize', onLineWrapDoSomething)
function onLineWrapDoSomething() {
const { lineHeight } = getComputedStyle(h1);
const lineHeightParsed = parseInt(lineHeight.split('px')[0]);
const amountOfLinesTilAdjust = 2;
if (h1.offsetHeight >= (lineHeightParsed * amountOfLinesTilAdjust)) {
console.log('your h1 now wrapped')
} else {
console.log('your h1 on one line')
}
}
// shows it logs when style changes and it wraps, ignore the disgusting code below
setTimeout(() => {
banner.style.width = '50%'
setTimeout(() => {
banner.style.width = '100%'
}, 1500)
}, 1500)
.banner {
width: 100%;
}
h1 {
line-height: 1.5
}
<div class="banner">
<h1>This is some text that will eventually wrap</h1>
</div>
I need to make it working for all my elements under Which nothing should fade (images).
Currently works only for the div.logo tag
I guess at the moment the reason mine <h1> element is now allowing animations underneath is that when I am getting the .top(), .left() and so on, you are doing it for the last element in the list.
I need to get the borders of each element in the resulting list.
Any help would be much appreciated
Demo: http://jsfiddle.net/z9b8S/
JS:
function displayThese(selectorString) {
var $heading = $(selectorString);
var h1top = $heading.position().top;
var h1bottom = h1top + $heading.height();
var h1left = $heading.position().left;
var h1right = h1top + $heading.width();
var divs = $('li').filter(function () {
var $e = $(this);
var top = $e.position().top;
var bottom = top + $e.height();
var left = $e.position().left;
var right = left + $e.width();
return top > h1bottom || bottom < h1top || left > h1right || right < h1left;
});
return divs;
}
(function fadeInDiv() {
var divsToChange = displayThese('h1, div.logo');
var elem = divsToChange.eq(Math.floor(Math.random() * divsToChange.length));
if (!elem.is(':visible')) {
elem.prev().remove();
elem.animate({
opacity: 1
}, Math.floor(Math.random() * 1000), fadeInDiv);
} else {
elem.animate({
opacity: (Math.random() * 1)
}, Math.floor(Math.random() * 1000), function () {
window.setTimeout(fadeInDiv);
});
}
})();
$(window).resize(function () {
// Get items that do not change
var divs = $('li').not(displayThese());
divs.css({
opacity: 0.3
});
});
The script below creates a slider widget the takes a definition list and turns it into a slide deck. Each dt element is rotated via css to become the "spine", which is used to reveal that dt's sibling dd element.
What I'm trying to do is to enhance it so that I can have the option to remove the spines from the layout and just use forward and back buttons on either side of the slide deck. To do that, I set the dt's to display:none via CSS and use the code under the "Remove spine layout" comment to test for visible.
This works fine to remove the spines, now I need to dynamically create 2 absolutely positioned divs to hold the left and right arrow images, as well as attach a click handler to them.
My first problem is that my attempt to create the divs is not working.
Any help much appreciated.
jQuery.noConflict();
(function(jQuery) {
if (typeof jQuery == 'undefined') return;
jQuery.fn.easyAccordion = function(options) {
var defaults = {
slideNum: true,
autoStart: false,
pauseOnHover: true,
slideInterval: 5000
};
this.each(function() {
var settings = jQuery.extend(defaults, options);
jQuery(this).find('dl').addClass('easy-accordion');
// -------- Set the variables ------------------------------------------------------------------------------
jQuery.fn.setVariables = function() {
dlWidth = jQuery(this).width()-1;
dlHeight = jQuery(this).height();
if (!jQuery(this).find('dt').is(':visible')){
dtWidth = 0;
dtHeight = 0;
slideTotal = 0;
// Add an element to rewind to previous slide
var slidePrev = document.createElement('div');
slidePrev.className = 'slideAdv prev';
jQuery(this).append(slidePrev);
jQuery('.slideAdv.prev').css('background':'red','width':'50px','height':'50px');
// Add an element to advance to the next slide
var slideNext = document.createElement('div');
slideNext.className = 'slideAdv next';
jQuery(this).append(slideNext);
jQuery('.slideAdv.next').css('background':'green','width':'50px','height':'50px');
}
else
{
dtWidth = jQuery(this).find('dt').outerHeight();
if (jQuery.browser.msie){ dtWidth = jQuery(this).find('dt').outerWidth();}
dtHeight = dlHeight - (jQuery(this).find('dt').outerWidth()-jQuery(this).find('dt').width());
slideTotal = jQuery(this).find('dt').size();
}
ddWidth = dlWidth - (dtWidth*slideTotal) - (jQuery(this).find('dd').outerWidth(true)-jQuery(this).find('dd').width());
ddHeight = dlHeight - (jQuery(this).find('dd').outerHeight(true)-jQuery(this).find('dd').height());
};
jQuery(this).setVariables();
// -------- Fix some weird cross-browser issues due to the CSS rotation -------------------------------------
if (jQuery.browser.safari){ var dtTop = (dlHeight-dtWidth)/2; var dtOffset = -dtTop; /* Safari and Chrome */ }
if (jQuery.browser.mozilla){ var dtTop = dlHeight - 20; var dtOffset = - 20; /* FF */ }
if (jQuery.browser.msie){ var dtTop = 0; var dtOffset = 0; /* IE */ }
if (jQuery.browser.opera){ var dtTop = (dlHeight-dtWidth)/2; var dtOffset = -dtTop; } /* Opera */
// -------- Getting things ready ------------------------------------------------------------------------------
var f = 1;
var paused = false;
jQuery(this).find('dt').each(function(){
jQuery(this).css({'width':dtHeight,'top':dtTop,'margin-left':dtOffset});
// add unique id to each tab
jQuery(this).addClass('spine_' + f);
// add active corner
var corner = document.createElement('div');
corner.className = 'activeCorner spine_' + f;
jQuery(this).append(corner);
if(settings.slideNum == true){
jQuery('<span class="slide-number">'+f+'</span>').appendTo(this);
if(jQuery.browser.msie){
var slideNumLeft = parseInt(jQuery(this).find('.slide-number').css('left'));
if(jQuery.browser.version == 6.0 || jQuery.browser.version == 7.0){
jQuery(this).find('.slide-number').css({'bottom':'auto'});
slideNumLeft = slideNumLeft - 14;
jQuery(this).find('.slide-number').css({'left': slideNumLeft})
}
if(jQuery.browser.version == 8.0 || jQuery.browser.version == 9.0){
var slideNumTop = jQuery(this).find('.slide-number').css('bottom');
var slideNumTopVal = parseInt(slideNumTop) + parseInt(jQuery(this).css('padding-top')) - 20;
jQuery(this).find('.slide-number').css({'bottom': slideNumTopVal});
slideNumLeft = slideNumLeft - 10;
jQuery(this).find('.slide-number').css({'left': slideNumLeft})
jQuery(this).find('.slide-number').css({'marginTop': 10});
}
} else {
var slideNumTop = jQuery(this).find('.slide-number').css('bottom');
var slideNumTopVal = parseInt(slideNumTop) + parseInt(jQuery(this).css('padding-top'));
jQuery(this).find('.slide-number').css({'bottom': slideNumTopVal});
}
}
f = f + 1;
});
if(jQuery(this).find('.active').size()) {
jQuery(this).find('.active').next('dd').addClass('active');
} else {
jQuery(this).find('dt:first').addClass('active').next('dd').addClass('active');
}
jQuery(this).find('dt:first').css({'left':'0'}).next().css({'left':dtWidth});
jQuery(this).find('dd').css({'width':ddWidth,'height':ddHeight});
// -------- Functions ------------------------------------------------------------------------------
jQuery.fn.findActiveSlide = function() {
var i = 1;
this.find('dt').each(function(){
if(jQuery(this).hasClass('active')){
activeID = i; // Active slide
} else if (jQuery(this).hasClass('no-more-active')){
noMoreActiveID = i; // No more active slide
}
i = i + 1;
});
};
jQuery.fn.calculateSlidePos = function() {
var u = 2;
jQuery(this).find('dt').not(':first').each(function(){
var activeDtPos = dtWidth*activeID;
if(u <= activeID){
var leftDtPos = dtWidth*(u-1);
jQuery(this).animate({'left': leftDtPos});
if(u < activeID){ // If the item sits to the left of the active element
jQuery(this).next().css({'left':leftDtPos+dtWidth});
} else{ // If the item is the active one
jQuery(this).next().animate({'left':activeDtPos});
}
} else {
var rightDtPos = dlWidth-(dtWidth*(slideTotal-u+1));
jQuery(this).animate({'left': rightDtPos});
var rightDdPos = rightDtPos+dtWidth;
jQuery(this).next().animate({'left':rightDdPos});
}
u = u+ 1;
});
setTimeout( function() {
jQuery('.easy-accordion').find('dd').not('.active').each(function(){
jQuery(this).css({'display':'none'});
});
}, 400);
};
jQuery.fn.activateSlide = function() {
this.parent('dl').setVariables();
this.parent('dl').find('dd').css({'display':'block'});
this.parent('dl').find('dd.plus').removeClass('plus');
this.parent('dl').find('.no-more-active').removeClass('no-more-active');
this.parent('dl').find('.active').removeClass('active').addClass('no-more-active');
this.addClass('active').next().addClass('active');
this.parent('dl').findActiveSlide();
if(activeID < noMoreActiveID){
this.parent('dl').find('dd.no-more-active').addClass('plus');
}
this.parent('dl').calculateSlidePos();
};
jQuery.fn.rotateSlides = function(slideInterval, timerInstance) {
var accordianInstance = jQuery(this);
timerInstance.value = setTimeout(function(){accordianInstance.rotateSlides(slideInterval, timerInstance);}, slideInterval);
if (paused == false){
jQuery(this).findActiveSlide();
var totalSlides = jQuery(this).find('dt').size();
var activeSlide = activeID;
var newSlide = activeSlide + 1;
if (newSlide > totalSlides) {newSlide = 1; paused = true;}
jQuery(this).find('dt:eq(' + (newSlide-1) + ')').activateSlide(); // activate the new slide
}
}
// -------- Let's do it! ------------------------------------------------------------------------------
function trackerObject() {this.value = null}
var timerInstance = new trackerObject();
jQuery(this).findActiveSlide();
jQuery(this).calculateSlidePos();
if (settings.autoStart == true){
var accordianInstance = jQuery(this);
var interval = parseInt(settings.slideInterval);
timerInstance.value = setTimeout(function(){
accordianInstance.rotateSlides(interval, timerInstance);
}, interval);
}
jQuery(this).find('dt').not('active').click(function(){
var accordianInstance = jQuery(this); //JSB to fix bug with IE < 9
jQuery(this).activateSlide();
clearTimeout(timerInstance.value);
timerInstance.value = setTimeout(function(){
accordianInstance.rotateSlides(interval, timerInstance);
}, interval);
});
if (!(jQuery.browser.msie && jQuery.browser.version == 6.0)){
jQuery('dt').hover(function(){
jQuery(this).addClass('hover');
}, function(){
jQuery(this).removeClass('hover');
});
}
if (settings.pauseOnHover == true){
jQuery('dd').hover(function(){
paused = true;
}, function(){
paused = false;
});
}
});
};
})(jQuery);
Creating elements in jQuery is easy:
$newDiv = $('<div />');
$newDiv.css({
'position': 'absolute',
'top': '10px',
'left': '10px'
});
$newDiv.on('click', function() {
alert('You have clicked me');
});
$('#your_container').append($newDiv);