Binding to lines in flowchart.js - javascript

I'm using flowchart.js to create diagrams. I want to be able to bind a click event to each arrow on the diagram to perform some action to that unique path.
As far as I can see in the inspector, these lines don't have IDs... Is there a way to get around this?

I verified what you wrote and you're correct, the lines (svg paths) don't have IDs. Also, it looks like they don't have much of a programming API. So I came up with a hacky way of assigning IDs for each path.
After you make your call to diagram.drawSVG(...); add the following code create unique IDs for each path.
var i = 0;
$("path").each(function() {
$(this).attr("id", "path" + i.toString());
i++;
});
I then added a click handler to the path elements to verify the IDs were properly assigned.
$(document).on("click", "path", function () {
//Display to the console
var clickedPath = $(this)[0];
console.log(clickedPath);
// Build a string of attributes by looping them
var alertString="";
for (i=0; i < clickedPath.attributes.length; i++) {
alertString += clickedPath.attributes[i].name + "=" + clickedPath.attributes[i].value + "\n";
}
//Alert the attributes
alert(alertString);
});
You can view the results at https://codepen.io/anon/pen/EoqPQG?editors=0010

Related

JavaScript - Find ID that matches data attribute value

I have a variable that finds the data attribute of an element that is clicked on in a callback function:
var dropdown = document.getElementsByClassName('js-dropdown');
for (i = 0; i < dropdown.length; i++) {
dropdown[i].addEventListener("click", callBack (dropdown[i]));
}
function callBack (i) {
return function () {
var thisDropdown = i.getAttribute('data-dropdown');
//rest of the code here
}
}
I am basically trying to do this
$('#' + thisDropdown ).toggleClass('is-active');
...but in vanilla JS.
This works fine using jQuery however I would like a vanilla version.
So when a user clicks on an element that activates a drop down, I want it to dynamically find its relevant ID matching value within the document so it can toggle a show/hide class.
I've searched through a lot of SO questions and everyone replies with a jQuery answer which is not what I am looking for.
I've been trying to do something along the lines of
var idValue = document.getElementById(thisDropdown);
Then
var findId= idValue + thisDropdown;
findId.toggleClass('is-active');
Obviously that does not work the same way the jQuery statement works... any ideas?
Ignore the toggleClass method! Some of you may find this contradictory as I want vanilla JS.
To replace $('#' + thisDropdown ).toggleClass('is-active'); with plain js, use Element.classList. Like this:
const someElement = document.querySelector('#' + thisDropdown);
someElement.classList.toggle("is-active");
I like #kamyl's answer, but you might need backward compatibility. For that, see if you can find a polyfill.
If you have to write it yourself, use string.split(" ") to get your list of active attributes and iterate to find if it exists; add if not, remove if so...then array.join(" ") and replace the class attribute with it.

Storing data with nested .each() function causes to replicate values

I've modified a survey-multi-choice plugin from JsPsych, in order to get responses in the form of checkboxes, instead of radio-buttons, since I need to present an image to the user, followed by 4 alternatives, like this:
Where A, B, C and D are also images, with their respective checkbox below each one. This structure must be presented more than once, like this:
So in this example, the expected output would be:
{"Q0":["Option A","Option B"]} //this is the first "question" displayed
{"Q1":["Option B","Option C"]} //second one
{"Q2":["Option B","Option D"]} //third one
But instead I get the first answer replicated for the rest of the questions:
{"Q0":["Option A","Option B"]} //this is the first "question" displayed
{"Q1":["Option A","Option B"]} //second one
{"Q2":["Option A","Option B"]} //third one
My code is provided below:
$("div." + plugin_id_name + "-question").each(function(index) {
var id = "Q" + index;
var val = [];
var a = $(".jspsych-survey-multi-choicemrbeta-option");
$("input:checkbox:checked").each(function(){
val.push($(this).attr("value"));
});
var obje = {};
obje[id] = val;
$.extend(question_data, obje);
});
I've tried tracking down the values generated on each step by printing them on console, so I'm guessing the problem is how I'm implementing those nested loops, thus the name of this question.
I've tried different approaches when implementing this loop, without better results:
for (var j = 0; j < trial.options[index].length; j++) {
if ($('jspsych-survey-multi-choicemrbeta-response-' + j).is(':checked')) {
val.push($(this).attr("value"));
}
}
A working example of my full code can be found here, for you to test it (look for the jspsych-survey-multi-choicemrbeta.js file, from line #141). CSS isn't included so it'll look slightly different.
Please note that the output of this code is a CSV file, and the full set of responses is given on a single cell, since all those questions belongs to the same JsPsych "trial".
Thanks for your help.
The inner loop iterates over all checkboxes, not only the ones belonging to the question.
Assuming the checkboxes are descendants of the div for the associated question, you should change the inner loop from this:
$("input:checkbox:checked").each( ...
to this:
$(this).find("input:checkbox:checked").each( ...

Javascript function calling itself

I have a javascript function with two parameters : results which is an object array and i which is the index.
The function displays item from that array. I also want to to build links to show other entries in the array.
My code is:
function renderNews(results, i) {
$('.articleTitle').text(results[i].Title);
$('.articleBody').text(results[i].newsBody);
// Build links
var linkHtml = '';
for (var i = 0; i < results.length; i++) {
linkHtml += '' + (i + 1) + ' ';
}
$('.articleActions').html(linkHtml);
}
As you can see I am setting my onclick for the function to call itself to redraw the results. I get a "function not defined error".
I'm still very much learning as I go. Is it bad idea for a function to call itself? I wonder if anyone can advise on the right way of doing this.
If I understand, renderNews will be called when the page gets loaded, right? Actually, your links would be put inside a component with articleActions class. By your idea, clicking any link would call this function again, and all links would be replaced by a new links. This sounds strange. Also, I can't tell what do you expect when passing that results to the onclick event. Actually, if your idea was to always reuse the same results array, passing it undefinitely to the same function over and over again, you could make things much simpler:
function renderNews(results) {
if (results.length == 0)
return;
// Build links
var linkHtml = '';
for (var i = 0; i < results.length; i++)
linkHtml += '' + (i + 1) + ' ';
$('.articleActions').html(linkHtml);
$('.articleTitle').text(results[0].Title);
$('.articleBody').text(results[0].newsBody);
}
$('.article-link').click(function(){
$('.articleTitle').text($(this).data('articletitle'));
$('.articleBody').text($(this).data('articlebody'));
});
As far as I understand, whenever you want to update the current articles, you call renderNews which will build/rebuild a lot of links for each article in the array holding their data (title and body), and will load the first item. So renderNews is going to be called once the page loads (I don't know how you intend to do this).
There is a click event for any component with article-link class, in this case all your links (anchors). When one is clicked, it updates the screen (article's title and body) with its data.
You could improve the code to keep track of the selected item, and once renderNews is called, you load that article instead of the first one. Or you could keep passing the article's index as parameter, like your example.
Since I don't know how do you call renderNews function, it's hard to make a better code, but this might clear something to you.
Simple JSFiddle demo

Local storage not working issues with for loop

I am using local storage to allow the user to return to a form after it has been submitted and amend with previous values persisting.
I succeeded in using the jQuery Storage Api (for set() and get()) but only by writing out long hand for each form element, not ideal. Instead I wanted to push all the form element ids to an array and loop through the array. First part, pushing to the array, works like a charm but the for loop I used is not working.
I intend to use jQuery .each() function but want to understand why my loop is not working first. Thanks.
(function() {
var selectArray = [];
// Getting select ids
$("select").each(function() {
selectArray.push($(this).attr("id"));
});
// Using Local Storage Api
var storage = $.localStorage;
for (var i = 0; i < selectArray.length; i++){
// Get select element
$("#" + selectArray[i]).val(storage.get(selectArray[i]));
// Set select element
$("#" + selectArray[i]).change(function() {
var selectValue = $("#" + selectArray[i]).val();
storage.set(selectArray[i], selectValue);
});
// Check loop is working
console.log(i + ". " + selectArray[i]);
}
}());
Resources:
jQuery v1.11.0
jQuery Storage API
Just change the change event handler like this, avoid reference to i. variable i is getting dangled here.
$("#" + selectArray[i]).change(function() {
var selectValue = $(this).val();
storage.set($(this).attr('id'), selectValue);
});
This should work.
Example
You are right, inside the change handler function you refer to the loop variable 'i'. When the change handler is actually called, 'i' is dereferenced and always contains the last value: selectArray.length

Extract value from an attribute from each link and assign an onclick using the fetched value?

I'm learning JS but have hit a roadblock. I have links that have the attribute "number". I'd like to extract the value of "number" from each link, set it as a new variable, and then assign an onclick action to each link incorporating the corresponding value. I've been able to extract each value but don't know how to use them in the onclicks.
HTML
<a class="button call" href="#" number="6135555556">Call pager</a>
<a class="button call" href="#" number="6135555555">Call cell</a>
JS
var data = document.getElementsByClassName("call");
var numbers = '';
for (var i = 0; i < data.length; i++) {
numbers += data[i].getAttribute("number");
numbers[i].onclick = console.log("call " + numbers[i]);
}
If you want to the particular value on click of particular link then you can use this code.
var data = document.getElementsByClassName("call");
var numbers = [];
for (var i = 0; i < data.length; i++) {
data[i].onclick = getNumber;
}
function getNumber(){
numbers.push(this.dataset['number']);
alert(this.dataset['number']);
}
Here is the DEMO
There is no number property on anchor tag, so for your need we can use data-* property which allows you to store needful information on html.
This may not be entity correct, but assuming what you wanted was to console log the contained phone number whenever a link was clicked, there are probably 3 main changes you'd want to look at.
1) I'm guessing you wanted to connect your onclick event to the link element with the number in it data[i], rather to the number itself?
2) += will concatenate each found value on to the previous one. This may be what you wanted, although in the below code I've changed it only to log the current number
3) onclick expects to be passed a function, which it will then run when the click event is fired. Wrapping your console log in a function provides it to the onClick in the format it expects.
Assuming all that's right, the js to work with the above links should look something like this:
var data = document.getElementsByClassName("call");
for (var i = 0; i < data.length; i++) {
data[i].onclick = function() { console.log("call " + this.getAttribute("number")); }
}
Hope that helps :)
Edit: Updated the code to fix the bug james montagne pointed out below. The getAttribute is now performed within the context of the click event, meaning the issue with scoping is avoided. Sorry about that, completely missed the issue.

Categories