Adding additional data to Raphael.js text element - javascript

I am working on some sort of graph in Raphael.js, I made it first with D3 (which can be found here), but that didn't work as well in Internet Explorer. I've heard Raphael.js does it much better in IE.
So now I am trying to convert my D3 code to Raphael, I made some progress, but I have some problems trying to add additional data or an id to a text element, which I can use to modify the text of a specific element. In D3 I've used .attr("weight", weight) on a text element, and this works fine. But in Raphael, I can't get it to work.
I've tried giving the text an ID like this:
var text = paper.text((y * 50) + 20, (i * 50) + 15, weight).attr({
fill: '#000'
});
text.attr({
"font-size": 16,
"font-family": "Arial, Helvetica, sans-serif"
});
text.id = weight;
But still no luck in logging that ID. If it is not clear why I need this ID, check the before mentioned jsFiddle I made with D3, I need to get a specific element to change its text.
var grossRisks = [{
"weight": "1",
"number": "5"
}, {
"weight": "4",
"number": "6"
}];
function populateChart(riskArray) {
var element = document.getElementsByTagName("text"),
attr;
var loops = riskArray.length;
for (var i = 0; i < loops; i++) {
var obj = riskArray[i];
for (var x = 0; x < element.length; x++) {
attr = element[x];
if (/^text/.test(attr.nodeName)) {
console.log(
"Weight: " + attr.getAttribute("weight"));
if (attr.getAttribute("weight") == obj.weight) {
attr.textContent = obj.number;
}
}
}
}
I have tried multiple things like making a new set and adding data to that set, but I don't know where to go from that point, like how do I change the text of a set with a specific ID?
newSet[i] = paper.set();
newSet[i].push(rect, text);
newSet[i].attr({
cursor: 'pointer'
}).data('weight', weight);
newSet[i].click(function () {
console.log(this.id("weight"));
});
The not working code of Raphael can be found in this jsFiddle.

The issues are
You get the raw DOM nodes, and not the RaphaelJS wrapped objects (because you use document.getElementsByTagName)
You set data but try to access it through attr.
In the fiddle, the newSet is not correctly built becuase you use the i variable which goes from 1 to 5.. to calculate the index you need to account for both of the loop counter.
So the changes made are
When populating the newSet
var index = (i*5) + y;
newSet[index] = paper.set();
When clearing and populating the chart use newSet
newSet[x].forEach(function(item){
if (item.type=='text')
attr=item;
});
to get the text node.
Working demo at Have a look at http://jsfiddle.net/G94sQ/22/
You can ofcourse use id to simplify the code
To assign an id just use text.id = 'your-id'
When creating the newSet
text.id = 'weight-'+weight;
and when clearing/populating
attr = paper.getById('weight-'+obj.weight);
Working demo at http://jsfiddle.net/G94sQ/23/
(additionally: you are using jquery 1.11 in which the .toggle method just shows/hide the element and does not rotate the click functions as earlier versions. So I changed your code to 1.4.2 as a quick fix..)

Related

How to change data content color of a vis js time line

In my vueJS application I'm using vis.js time line to show some data,
I have integrated the vis timeline with some randomly generated data.
Following is my code for that,
created() {
var now = moment().minutes(0).seconds(0).milliseconds(0);
var groupCount = 3;
var itemCount = 20;
// create a data set with groups
var names = ['John', 'Alston', 'Lee', 'Grant'];
for (var g = 0; g < groupCount; g++) {
this.groups.push({
id: g,
content: names[g]
});
}
// create a dataset with items
for (var i = 0; i < itemCount; i++) {
var start = now.clone().add(Math.random() * 200, 'hours');
var group = Math.floor(Math.random() * groupCount);
this.items.push({
id: i,
group: group,
content: 'item ' + i +
' <span class="" style="color:green;">(' + names[group] + ')</span>',
start: start,
type: 'box',
className: 'green', //green or yellow
title:'Testing tool tip index' + i
});
}
},
Then I used
<timeline ref="timeline"
:items="items"
:groups="groups"
:options="options"
>
</timeline>
to display the time line in my vue component.
This outputs me something like follows,
Please kindly note I have added some custom css changes to change the initial look and feel of the time line.
Since this is my first experience working with vis, I want to know, how can I change the colors of those bars...
How can I assign random color to those bars instead of all green....
I found the best way to do this was through the item's className property. So as an example, you might assign 'colour_1', 'colour_2' 'colour_3' randomly inside your 'create a dataset with items' loop.
Then provide a matching stylesheet that looks like
.colour_1 {background-color:#7fc97f; border-color:#7fc97f}
.colour_2 {background-color:#beaed4; border-color:#beaed4}
.colour_3 {background-color:#fdc086; border-color:#fdc086}
This mechanism can be extended further with additional classes applied to the container to 'switch in or out' different groups of colours depending on what you want to see.
Here's a screenshot of my (non-random) colourised timeline using this technique.
You /could/ alternatively use the 'template' function override to achieve a similar goal, but I found that the call to 'redraw' was a lot slower than just switching between colour palettes in CSS.

Endpoint no longer drags with Anchor after Anchor ID is changed after creation - JSPlumb

I'm building an editor and when I place a shape (also requires entering ID for that shape) onto the canvas, endpoints get created and attached to the shape. The ID of the shape is used for the endpoints to anchor onto.
//Function which anchors endpoints onto shapes.
var _addEndpoints = function (sourceAnchors, targetAnchors, id) {
for (var i = 0; i < sourceAnchors.length; i++) {
var sourceUUID = sourceAnchors[i];
epp = jsPlumbInstance.addEndpoint(id, sourceEndpoint, {
anchor: sourceAnchors[i], uuid: sourceUUID
});
sourcepointList.push([id , epp]);
epp = null;
}
for (var j = 0; j < targetAnchors.length; j++) {
var targetUUID = targetAnchors[j];
epp = jsPlumbInstance.addEndpoint(id, targetEndpoint, {
anchor: targetAnchors[j], uuid: targetUUID
});
endpointList.push([id, epp]);
epp = null;
}
};
function drawElement(element, canvasId, id) {
$(canvasId).append(element);
_addEndpoints(properties[0].startpoints, properties[0].endpoints, id);
jsPlumbInstance.draggable(jsPlumbInstance.getSelector(".jtk-node"), {
grid: [20, 20]
});
}
This works perfectly fine as the endpoints work fine and drag with the shape, however one functionality for my editor I would like is being able to change the ID after clicking on the shape. When the ID is changed, the endpoints no longer drag with the shape. They do still drag on there own however. I have attempted to the following to update the ID in the endpoints by updating the anchor and element.
//My solution attempt
jsPlumbInstance.selectEndpoints().each(function(endpoint) {
endpoint.setElement(anchor); //anchor is the same anchor as before with updating ID.
endpoint.setAnchor(x, y, newID); //new ID is the new ID the anchor now has
});
However I get the following error.
jsPlumb function failed : TypeError: Cannot read property 'el' of undefined
Looking into the properties of the endpoints there are alot of properties which still have the old ID value, and I have no Idea how to update all of these at the same time. Can anyone please help me out?
Found a fix which was rather simple in the end, instead of what I was doing before, the following does the trick
jsPlumbInstance.setId(oldID, ID);
Instead of changing the anchorID manually and then doing the same for the endpoints, the above instance tells JSPlumb to change the ID for you while at the same time update the ID everywhere in JSPlumb.
A note on this for users of 4.x - jsPlumb no longer uses the DOM id internally, so this would not be an issue in 4.x.

PivotTable.js conditionally change color on text

So I'm working with PivotTable.js which has been a great help at work.
Right now though, I'm trying to get a filter going to change the color of cells or font within the cell depending on the value.
For example, if I have an array of dates in my dataset
dates = ["N/A", "4/12/2016", "7/9/2024", "7/9/2024", "4/1/2013"]
I want to make it so any dates before 6/1/2016 to change colors.
I have my data being passed in locally as a variable 'data' if that makes any difference
$(function(){
var derivers = $.pivotUtilities.derivers;
var renderes = $.extend($.pivoUtilities.renderers, $.pivotUtilities.export_renderers);
$("#pivot_table").pivotUI(data, {
derivedAttributes: function(data){
// not sure how to access the css of the element from here
}
rows: [],
cols: ["Name", "Date", "Organization", "Cost"],
renderers: renderers,
rendererName: "Table"
});
});
I've tried going into derivedAttributes, but everything I tried wasn't working.
Any help or brainstorming would be much appreciated on this
So...I actually solved it on my own haha...
One of the great things about PivotTable.js is the flexibility in options and sorting. So I used the onRefresh attribute and fed it this function
onRefresh: function() {
var $labels = $('.pvtRowLabel')
var today = new Date();
var d = today.getDate();
var m = today.getMonth()+1;
var y = today.getFullYear();
var date;
var dateReg = /^\d{1,2}[\/]\d{1,2}[\/]\d{4}$/;
// Goes through each cell with the class '.pvtRowLabel'
for (var i=0; i<$labels.length; i++) {
if ($labels[i].innerHTML.match(dateReg)) {
date = $labels[i].innerHTML.split('/');
if (Number(date[2]) == y) {
if (Number(date[0]) == m) {
if (Number(date[1]) <= d) {
$('.pvtRowLabel').eq(i).addClass('expired');
}
} else if (Number(date[0]) < m) {
$('.pvtRowLabel').eq(i).addClass('expired');
}
} else if (Number(date[2]) < y) {
$('.pvtRowLabel').eq(i).addClass('expired');
}
}
};
},
After that, just use a css selecter to specify the color you want to use
.expired { background-color: #F08080 !important; }
The problem with my solution is that it adds more strain on the browser since it's checking the DOM and adding classes every time the table is refreshed. I'm not sure if there's a way to accomplish this when it's first rendered, so those cells are always going to be labeled as expired when generated.
Here's one solution I found to change the font color of a single row in the table, say row no. 5:
$("#pivot-table").pivotUI(data, {
...
onRefresh: function (config) {
// Show row no.5 as red
$("#pivot-table").find('.pvtVal.row5').css('color', 'red');
},
...
});
I did custom coloring by editing the pivot.min.js file.
You may have to tweak the loop to segregate the data and add required css style in the js file.

Efficient way of changing background color of an element in CSS3 or HTML5

I need to change each item's color in a list after a reorder or removing one item, now I am using jquery's css method like below
$('li').css('background-color', color);
It works, but terribly slow, and sometimes the page will render the color incorrectly, even on Chrome, which is supposed to be fast. The list doesn't have many items, below 10, usually 5 - 7. So this performance is not acceptable. So I want to know if there is a better, faster way in CSS3, or HTML5. If not, if there is an walkaround or some kind of jquery solution?
The code for refreshing list items' color is as below. The index can be decided by a function and the color can decide color by it. The major issue I think is that changing background color trigger reflow or maybe rerendering.
function refreshListItemColor(liElements, colorGetter, indexGetter) {
colorGetter = colorGetter || (function (color) {
return color;
});
indexGetter = indexGetter || (function (liElement, index) {
return index;
});
liElements.each(function (index, liElement) {
index = indexGetter(liElement, index);
var data = ko.dataFor(liElement);
var indexColor = colorForIndex(index);
indexColor = colorGetter(indexColor, data);
if (indexColor !== $(liElement).css('background-color')) {
$(liElement).css('background-color', indexColor);
}
});
}
Update: using element.style['background-color'] won't do. The issue still remains. Another possible explanation for the lagging is that every list item itself has about 10 child elements, making change list item's color particularly expensive.
Update2: I'll try to ask a related question: is there a way to change the color of the background of the parent node without triggering a rerender of children elements?
Update3: I tried to add delay for each color change operation, like below
var delay = 100, step = 100;
liElements.each(function (index, liElement) {
index = indexGetter(liElement, index);
var data = ko.dataFor(liElement);
var indexColor = colorForIndex(index);
indexColor = colorGetter(indexColor, data);
if (indexColor !== $(liElement).css('background-color')) {
setTimeout(function () {
liElement.style['background-color'] = indexColor;
}, delay);
delay += step;
}
});
It seems can alleviate this issue a lot. I guess this will not solve the problem, but will reduce the impact to an acceptable level.
Could you use attribute selectors in your stylesheet?
[data-row="1"][data-col="3"]{
background-color: blue;
}
I noticed that If you want to select a whole row or column you have to use !important
[data-col="3"]{
background-color: blue !important;
}
(edit)Adding styles dynamically
Create a empty style tag with a div
<style type="text/css" id="dynamicstyle"></style>
and just append to it like any other tag
$("#dynamicstyle").append('[data-row="0"]{background-color:red !important;}');
for your case you can check whenever an element is added and add a row style since in theory the user could pile up all of the elements.
$(function () {
var maxRows = 0;
$("ul").bind("DOMSubtreeModified", updateStyleSheet);
function updateStyleSheet() {
var childCount = $("ul").children().length;
if (maxRows < childCount) {
maxRows = childCount;
var newRule = [
'[data-row="',
maxRows,
'"]{background-color:', ((maxRows % 2) ? "red" : "blue"),
' !important;}'].join('')
$("#dynamicstyle").append(newRule);
}
}
});
http://jsfiddle.net/PgAJT/126/
FizzBuzz rows http://jsfiddle.net/PgAJT/127/
Remove your "if", which may force browser to redraw/recompile/reflow latest CSS value.
if (indexColor !== $(liElement).css('background-color')) {
Yes, read are slow and as they will block write-combine.
Presumably, the colour is determined by the position of the element in the list.
Use nth-child or nth-of-type selectors in your stylesheet.
Hi i have just tried wat u need just check it..
http://jsbin.com/awUWAMeN/7/edit
function change()
{
var colors = ['green', 'red', 'purple'];
alert(colors)
$('.sd-list li').each(function(i) {
var index = $(this).index();
$(this).css('background-color', colors[index]);
});
}
I've created a simple test with 10 list items, each with 12 children and setting the background colour for every item each time Gridster's draggable.stop event fires. The change is pretty much instantaneous in IE11 and Chrome.
To me, this suggests it isn't the CSS rendering that's slow, but maybe the calculations determining which colours are for which elements.
This is the JavaScript I was using:
var colors = ['#000', '#001', '#002', '#003', '#004', '#005', '#006', '#007', '#008', '#009', '#00a', '#00b'];
$('.gridster ul').gridster({
widget_margins: [10, 10],
widget_base_dimensions: [120, 120],
draggable: {
stop: function (e, ui, $widget) {
refreshListItemColor();
}
}
});
function refreshListItemColor() {
var sortedElements = [];
$('ul > li:not(.preview-holder').each(function () {
sortedElements.push([this, this.getAttribute('data-col'), this.getAttribute('data-row')]);
});
sortedElements.sort(function (a, b) {
return a[1] - b[1] || a[2] - b[2];
});
for (var i = sortedElements.length - 1; i >= 0; i--) {
sortedElements[i][0].style.backgroundColor = colors[i];
}
}
How are you determining which colours to set on each list item?
I've find it fast to create a class with the css attributes you want and then add that class to the dom element you want the css attribute applied to. CSS rules appear without refresh.
css:
.bg-green{
background:green;
}
js:
$("#someDomId").toggleClass('bg-green','add');
A cool way of dealing with lists is to index the id of each list element as you create/alter it:
Create list:
for (i=0;i=m;i++){
var listElement = "<li id='"+i+">Some Content</div>";
$('ul').append(listElement);
}
Then instead of iterating through a dom element (which is expensive) you can run another for loop and alter each list element by selecting it's id.
for (i=0;i=m;i++){
$("#"+i).toggleClass('bg-green','add');
}

How to improve jQuery performance with appending icons?

I am building a spreadsheet editor with jQuery and I am encountering performance issues with big tables.
The table holds many data sets and when clicked on one, icons are added to the first cell of the other sets. The code looks like this:
$('.click_icon').remove();
for (var i = 0; i < datasets.length; i++) {
var first_cell = $('td.content[dataset="' + datasets[i].id + '"]').filter(':first');
if (in_group(datasets[i].id)) {
first_cell.append('<i class="icon-remove click_icon remove_group" style="float:right"></i>');
} else {
first_cell.append('<i class="icon-magnet click_icon add_group" style="float:right"></i>');
}
with 500+ datasets this takes about 5 seconds. in_group() is just a small function which checks if the set is in a group with the selected data set.
I was wondering if creating the icons prior to the click and using show() hide() would be faster? Any other ideas?
I am using Chromium on Ubuntu. (Version 28.0.1500.52 Ubuntu 12.04)
Build the table in memory and only change the DOM once :
$('.click_icon').remove();
var table = $('table');
var clone = table.clone(true);
for (var i = 0; i < datasets.length; i++) {
var _class = in_group(datasets[i].id) ?
'icon-remove click_icon remove_group' :
'icon-magnet click_icon add_group',
elem = $('<i />', {'class': _class, style:'float:right'});
$('td.content[dataset="' + datasets[i].id + '"]', clone).filter(':first')
.append(elem);
}
table.replaceWith(clone);
This is a general example, you may have to adjust this to make it work properly with your markup.
I'd normally use plain JS for performance, and documentFragments, but I think jQuery uses fragments internally when doing it this way.
In my experience, appending element by string is worst for performance than creating element by DOM.
So try anything like this:
if (in_group(datasets[i].id)) {
var i = document.createElement('i');
i.className = 'icon-remove click_icon remove_group';
i.style.float = 'right';
first_cell.appendChild(i);
}
If you expect the users to click, I think creating the icons prior to the click is a good idea.
Instead of show/hide which toggles the display property, you could use the visibility property. When the visibility changes from hidden to visible, the browser doesn't need to recalculate the layout.

Categories