D3.js How to update a global variable from d3.select() - javascript

I'm stuck with a global/local variable issue.
Here, my code based on http://www.d3noob.org/2014/04/using-html-inputs-with-d3js.html
index.html
<!DOCTYPE html>
<html lang="en">
<meta charset="utf-8">
<title>Input test (circle)</title>
<head>
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<p>
<label for="nRadius"
style="display: inline-block; width: 240px; text-align: right">
radius = <span id="nRadius-value">…</span>
</label>
<input type="range" min="1" max="150" id="nRadius">
</p>
<script src="./slider.js"></script>
</body>
</html>
slider.js
// update the circle radius
function update(nRadius) {
// adjust the text on the range slider
d3.select("#nRadius-value").text(nRadius);
d3.select("#nRadius").property("value", nRadius);
// update the circle radius
circle.selectAll("circle").attr("r", nRadius);
return nRadius;
}
var width = 600;
var height = 300;
var circle = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
// Circle with no radius
circle.append("circle")
.attr("cx", 300)
.attr("cy", 150)
.style("fill", "none")
.style("stroke", "blue");
// Initial radius
update(50);
// when the input range changes update the circle
d3.select("#nRadius").on("input", function() {
update(+this.value);
});
This code works just fine, but what I'd like to do is to be able to export 'this.value' outside the d3.select, in order to use it in some other function.
The d3.select showed below already includes some suggestions found by googling my question:
var currentRadius = 0;
d3.select("#nRadius").on("input", function() {
currentRadius = +this.value
console.log("Inside: " + currentRadius);
currentRadius = update(currentRadius);
});
console.log("Outside: " + currentRadius);
But it doesn't work.

The problem is here
//this is your global variable
var currentRadius = 0;
d3.select("#nRadius").on("input", function() {
//you are changing the global value here on change event.
currentRadius = +this.value
console.log("Inside: " + currentRadius);
currentRadius = update(currentRadius);
//call your function
outside();
});
//some function somewhere
function outside(){
console.log(currentRadius)
}
//value of global variable when this executed is 0
//because no change event has been called
console.log("Outside: " + currentRadius);
So in short the global variable change will happen in the change function.
And the console.log("Outside: " + currentRadius); is done much before that event got fired.
I hope this helps

Related

Adding text that appears and disappears after a certain time, at specific positions

I am trying to add text above the first circle that says "Stage 1" for 1 to 1.5s, then disappears and then another text "Stage 2" appears above the final position of the circle, that then disappears after 1 to 1.5s as well.
Right now, I have the text working, but not in the correct position and it does not disappear as well. Any help will be massively appreciated! Thanks :)
Code:
<meta charset="utf-8">
<head>
<title>D3 Starter Fun Time!</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<link rel="stylesheet" href="./styles/simple-style.css">
<link rel="icon" href="./favicon/favicon.ico">
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<clipPath id ="myClip">
<div id="chartDiv"></div>
</clipPath>
<div id="otherDiv"></div>
<button id="next">Play Animation</button>
<div id="changeText"></div>
<script>
var duration = 4000;
var svgContainer = d3.select("body").append("svg")
.attr("width", 800)
.attr("height", 400);
//Draw the Circle
var circle = svgContainer.append("circle")
.attr("cx", 90)
.attr("cy", 90)
.attr("fill","black")
.attr("r", 80);
var text = ["Stage 1", "Stage 2"];
var counter = 0;
var elem = document.getElementById("changeText");
//var inst = setInterval(change, 1000);
elem.innerHTML = text[0];
function change(flag)
{
elem.innerHTML = text[1];
}
document.getElementById("next").onclick = function() {linearReductionMed(600, 20)};
function linearReductionMed(x, r){
circle.transition()
.duration(duration)
.attr("cx", x)
.attr("r", r)
setTimeout(function(){ change(); }, duration);
}
function linearReductionSlow(x, r){
circle.transition()
.duration(duration*2)
.attr("cx", x)
.attr("r", r)
setTimeout(function(){ change(); }, duration*2);
}
function linearReductionFast(x, r){
circle.transition()
.duration(duration/2)
.attr("cx", x)
.attr("r", r)
setTimeout(function(){ change(); }, duration/2);
}
</script>
</body>
</html>
To make the text disappear you could use elem.innerHTML = "". For instance:
function show() {
elem.innerHTML = text;
setTimeout(hide, 1000);
}
function hide() {
elem.innerHTML = '';
}
To change the text position add some styling to <div id="changeText"> on the css, for instance (change according to what you need):
div#changeText {
float: left;
left: 300px;
top: 100px;
}

d3: hovering over words in text at the same time highlighting the corresponding bars and make word in text clickable

Dear Stackoverflow community
I am still a great beginner when it comes to D3 and JavaScript. I could implement my code in a way that I can upload a text file, transfer to the content to the next page, and render the bar charts. However I still do not have any clue how to realize the following function. Whenever the user hover over a word in the text the corresponding bar should be highlighted in a certain color. Also, how can I make the words in the text clickable. I hope you can help me out. I could not find anything appropriate on the internet to solve these problems.
Thank you very much.
Here is my code:
Loading the text file:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link href="https://unpkg.com/filepond/dist/filepond.css" rel="stylesheet">
<script src="https://d3js.org/d3.v5.min.js"></script>
<meta charset="UTF-8">
<title>File Upload</title>
<style>
.filepond--root {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol';
}
.filepond--drop-label {
color: #000033;
}
.filepond--panel-root {
background-color: transparent;
border: 1px solid #6666ff;
}
.filepond--panel-root {
background-color: #e5e5ff;
}
.filepond--label-action {
text-decoration-color: #a7a4a4;
}
/* the color of the focus ring */
.filepond--file-action-button:hover,
.filepond--file-action-button:focus {
box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.9);
}
h2 {
width: 100%;
text-align: center;
font-size: 17px;
border-bottom: 1px solid #939393;
line-height: 0.1em;
margin: 10px 0 20px;
}
h2 span {
background:#fff;
padding:0 10px;
}
</style>
</head>
<body>
<input type="file" class="filepond" id="fileInput">
<h2><span>OR</span></h2>
<form>
<div class="form-group">
<label for="textArea1">Paste your text here</label>
<textarea class="form-control" id="textArea1" rows="3"></textarea>
</div>
</form>
<button type="button" class="btn btn-outline-primary" onclick ="myFunction()">Submit</button>
<script src="https://unpkg.com/filepond/dist/filepond.js"></script>
<script>
FilePond.parse(document.body);
document.getElementById("fileInput").addEventListener("change",getF);
var res;
function getF(event) {
// check if textarea empty
var input = event.target;
readContentFile(input.files[0]).then(content => {
res = content;
getContent(res);
}).catch(error => console.log(error));
}
function readContentFile(file){
var reader = new FileReader();
return new Promise((resolve,reject) =>{
reader.onload = event => resolve(event.target.result);
reader.onerror = error => reject(error);
reader.readAsText(file, 'ISO-8859-1');
})
}
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g,"");
};
function getContent(file) {
localStorage.setItem("fileContent", file);
return file;
}
function myFunction(){
if (!(document.getElementById("textArea1").value.trim() == '')) {
localStorage.setItem("fileContent", document.getElementById("textArea1").value);
}
location.href="AnotherPage.html";
}
</script>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
</body>
</html>
The page with the graph and the text:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<script src="https://d3js.org/d3.v5.min.js"></script>
<meta charset="UTF-8">
<title>AnotherPage</title>
<style>
.row {
margin-top: 30px;
}
[class^='col'], [class*=' col'] {
margin-top: 30px;
}
#blockText{
border-left: 1.5px solid black;
}
</style>
</head>
<body>
<div class="container text-center">
<div class="row">
<div class="col-md-6" id="rectsBlock">
<svg id="barchart"></svg>
</div>
<div class="col-md-6" id="blockText">
</div>
</div>
</div>
</body>
<script>
var textContent, textDiv;
textContent = document.createTextNode(localStorage.getItem("fileContent"));
textDiv = document.getElementById("blockText");
textDiv.appendChild(textContent);
// extracting words
// var engPattern = /\w+/g;
var engPattern = /\b[^\d\W]+\b/g;
//var krPattern = /[\uac00-\ud7af]|[\u1100-\u11ff]|[\u3130-\u318f]|[\ua960-\ua97f]|[\ud7b0-\ud7ff]/g;
var res = localStorage.getItem("fileContent").match(engPattern);
// console.log("Sprache"+localStorage.getItem("fileContent").match(krPattern))
var unique = res.reduce(function (a, b) {
if (a.indexOf(b) < 0)
a.push(b);
return a;
}, []);
function removeOneLetterElem(arr){
var index = 0;
for(var i = 0;i<arr.length;i++){
if(arr[i].length ===1 && arr[i]!=="a"){
console.log("Wert ist "+arr[i]);
index = arr.indexOf(arr[i]);
console.log(index);
arr.splice(index,1);
}
}
}
removeOneLetterElem(unique);
console.log(unique);
//function distinguishLang(lang){
//}
console.log("Value is"+document.getElementsByClassName("bar").textContent);
// percentages
var arrNums = [];
generatePercentages();
function generatePercentages(){
var arr = [];
for(var i = 0;i<unique.length;i++){
arr.push(Math.floor((Math.random()*100) + 1));
}
arrNums = arr;
}
// building json
var jsonData;
var wordset ={};
function buildJson(){
var words = [];
var word, val, wordElem;
wordset.words = words;
for(var i=0;i<unique.length;i++){
word = unique[i];
val = arrNums[i];
wordElem={
"word":word,
"value":val
};
wordset.words.push(wordElem);
}
jsonData = JSON.stringify(wordset);
}
buildJson();
// bar chars
console.log(wordset.words);
var margin = {top: 5, right: 80, bottom: 30, left: 90},
width = 600- margin.left - margin.right,
height = 1500 - margin.top - margin.bottom;
var svg = d3.select("#rectsBlock").select("#barchart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var x = d3.scaleLinear().range([0,width]);
var y = d3.scaleBand().range([height,0]);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
wordset.words.sort(function(a,b){
return a.value-b.value;
});
x.domain([0, d3.max(wordset.words, function(d){
return d.value;})]);
y.domain(wordset.words.map(function(d){
return d.word;
})).padding(0.2);
g.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")");
g.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y).tickSize(0));
var bars = g.selectAll(".bar")
.data(wordset.words)
.enter()
.append("rect")
.attr("class","bar")
.attr("x",0)
.attr("height",y.bandwidth())
.attr("y", function(d) { return y(d.word); });
bars.transition()
.duration(50)
.delay(function (d, i) {
return i * 20;
})
.attr("width", function(d){
return x(d.value);
})
.attr("fill","#1bf0c0");
g.selectAll(".text")
.data(wordset.words)
.enter()
.append("text")
.transition()
.duration(50)
.delay(function (d, i) {
return i * 20;
})
.attr("class","label")
.attr("y",function(d){
return y(d.word)+y.bandwidth()-1;
})
.attr("x",function(d){
return x(d.value)+3;
})
.attr("dy",".12em")
.style("font-size", "11px")
.text(function(d){
return d.value+"%";
});
bars.attr("id",function(d,i){
return i;})
.on("mouseover",function(d){
console.log("Printing words "+d.word);
d3.select(this)
.style("fill", "brown")
}).on("mouseout",function(d,i){
d3.select(this).style("fill","#1bf0c0");
});
d3.select("#blockText")
.on("click", function(){
alert("clicked");
});
</script>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
</body>
</html>
These are sample text files for testing :
https://ufile.io/xz9wd
Here's one approach to do that:
Instead of appending the whole text as content to the #blockText, creating span elements with the corresponding word stored as key would make it easier to add events to.
res.forEach(function (word) {
var span = document.createElement('span');
span.textContent = word + ' ';
span.setAttribute('data-key', word);
textDiv.appendChild(span);
});
Adding events: I'm using jQuery to add the mouseover, mouseout and click events to the above added spans. If you're not planning on including jQuery, you can always assign addEventListener to the newly created spans.
$('div#blockText span[data-key]').on('mouseover', function () {
var key = $(this).data('key');
d3.select("#rectsBlock").select("#barchart").select('svg rect[data-key='+key+']').style('fill', 'brown');
}).on('mouseout', function () {
var key = $(this).data('key');
d3.select("#rectsBlock").select("#barchart").select('svg rect[data-key='+key+']').style('fill', '#1bf0c0');
}).on('click', function () {
console.log("Clicked word: " + $(this).data('key'));
});
Fork of your JSFIDDLE: http://jsfiddle.net/j4qav07u/
Inline snippet:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.2.1.js"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
<meta charset="UTF-8">
<title>AnotherPage</title>
<style>
.row {
margin-top: 30px;
}
[class^='col'], [class*=' col'] {
margin-top: 30px;
}
#blockText{
border-left: 1.5px solid black;
}
</style>
</head>
<body>
<div class="container text-center">
<div class="row">
<div class="col-md-6" id="rectsBlock">
<svg id="barchart"></svg>
</div>
<div class="col-md-6" id="blockText">
</div>
</div>
</div>
</body>
<script>
var textContent, textDiv;
var fileContent = 'As ending of freedom of movement is central to the deal, some MPs have been calling for the government to publish the white paper for its post-Brexit immigration bill before next weekís meaningful vote.\n\nThe government has told those MPs this is not likely to happen. On Wednesday, the home secretary, Sajid Javid, said it was ìstill his intentionî to publish it this month ñ a choice of words opposition MPs met with groans and sighs.\n\nDefending the delay, Javid told MPs: ìItís worth keeping in mind that this is the biggest change in our immigration system in four decades. Itís important that we take the time and we get it right.î\n\nJavid also said freedom of movement would end, ìdeal or no dealî ñ in effect saying that while it was a key part of the deal, it was not strictly tied to what May was offering.\n\nHe also said MPs had been given glimpses of what the post-Brexit immigration system would look like.\n\nThe government is proposing a single immigration system that treats migrants from EU countries in the same way as those from non-EU countries. Highly skilled workers who want to live and work in Britain will be given priority.\n\nHowever, the government says the ability of people from abroad to deliver services and student exchange programmes will form part of future trade agreements.';
textContent = document.createTextNode(fileContent);
textDiv = document.getElementById("blockText");
//textDiv.appendChild(textContent);
// extracting words
// var engPattern = /\w+/g;
var engPattern = /\b[^\d\W]+\b/g;
//var krPattern = /[\uac00-\ud7af]|[\u1100-\u11ff]|[\u3130-\u318f]|[\ua960-\ua97f]|[\ud7b0-\ud7ff]/g;
var res = fileContent.match(engPattern);
res.forEach(function (word) {
var span = document.createElement('span');
span.textContent = word + ' ';
span.setAttribute('data-key', word);
textDiv.appendChild(span);
});
$('div#blockText span[data-key]').on('mouseover', function () {
var key = $(this).data('key');
d3.select("#rectsBlock").select("#barchart").select('svg rect[data-key='+key+']').style('fill', 'brown');
}).on('mouseout', function () {
var key = $(this).data('key');
d3.select("#rectsBlock").select("#barchart").select('svg rect[data-key='+key+']').style('fill', '#1bf0c0');
}).on('click', function () {
console.log("Clicked word: " + $(this).data('key'));
});
// console.log("Sprache"+localStorage.getItem("fileContent").match(krPattern))
var unique = res.reduce(function (a, b) {
if (a.indexOf(b) < 0)
a.push(b);
return a;
}, []);
function removeOneLetterElem(arr){
var index = 0;
for(var i = 0;i<arr.length;i++){
if(arr[i].length ===1 && arr[i]!=="a"){
//console.log("Wert ist "+arr[i]);
index = arr.indexOf(arr[i]);
//console.log(index);
arr.splice(index,1);
}
}
}
removeOneLetterElem(unique);
//console.log(unique);
//function distinguishLang(lang){
//}
//console.log("Value is"+document.getElementsByClassName("bar").textContent);
// percentages
var arrNums = [];
generatePercentages();
function generatePercentages(){
var arr = [];
for(var i = 0;i<unique.length;i++){
arr.push(Math.floor((Math.random()*100) + 1));
}
arrNums = arr;
}
// building json
var jsonData;
var wordset ={};
function buildJson(){
var words = [];
var word, val, wordElem;
wordset.words = words;
for(var i=0;i<unique.length;i++){
word = unique[i];
val = arrNums[i];
wordElem={
"word":word,
"value":val
};
wordset.words.push(wordElem);
}
jsonData = JSON.stringify(wordset);
}
buildJson();
// bar chars
// console.log(wordset.words);
var margin = {top: 5, right: 80, bottom: 30, left: 90},
width = 600- margin.left - margin.right,
height = 1500 - margin.top - margin.bottom;
var svg = d3.select("#rectsBlock").select("#barchart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var x = d3.scaleLinear().range([0,width]);
var y = d3.scaleBand().range([height,0]);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
wordset.words.sort(function(a,b){
return a.value-b.value;
});
x.domain([0, d3.max(wordset.words, function(d){
return d.value;})]);
y.domain(wordset.words.map(function(d){
return d.word;
})).padding(0.2);
g.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")");
g.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y).tickSize(0));
var bars = g.selectAll(".bar")
.data(wordset.words)
.enter()
.append("rect")
.attr("class","bar")
.attr("x",0)
.attr("height",y.bandwidth())
.attr("y", function(d) { return y(d.word); });
bars.transition()
.duration(50)
.delay(function (d, i) {
return i * 20;
})
.attr("width", function(d){
return x(d.value);
})
.attr("fill","#1bf0c0");
g.selectAll(".text")
.data(wordset.words)
.enter()
.append("text")
.transition()
.duration(50)
.delay(function (d, i) {
return i * 20;
})
.attr("class","label")
.attr("y",function(d){
return y(d.word)+y.bandwidth()-1;
})
.attr("x",function(d){
return x(d.value)+3;
})
.attr("dy",".12em")
.style("font-size", "11px")
.text(function(d){
return d.value+"%";
});
bars.attr("id",function(d,i){
return i;}).attr('data-key', function (d) { return d.word; })
.on("mouseover",function(d){
//console.log("Printing words "+d.word);
d3.select(this)
.style("fill", "brown")
}).on("mouseout",function(d,i){
d3.select(this).style("fill","#1bf0c0");
});
d3.select("#blockText")
.on("click", function(){
alert("clicked");
});
</script>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
</body>
</html>
Hope this helps.

How do I select an SVG inside an `object` tag?

Here's how HTML page's contents look.
<!DOCTYPE html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<object type="image/svg+xml" data="Map-edited.svg"></object>
<script src="script.js"></script>
</body>
</html>
And here's how script.js looks.
var tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("Hello");
var svg= d3.select("object").select("svg");
svg.selectAll('g')
.on("mouseover",function(){
return tooltip.style("visibility","visible");
})
.on("mousemove",function(){
var offset= 20;
var topPosition= (event.pageY-offset)+"px";
var leftPosition= (event.pageX+offset)+"px";
return tooltip.style("top",topPosition).style("left",leftPosition);
})
.on("mouseout", function(){
return tooltip.style("visibility","hidden");
});
When I open my HTML page, I see the SVG element with all the g elements in it. When I mouseover each g element, no tooltip appears. But if I replace object with the svg tag and its contents, the tooltip works. How do I make d3 select an SVG in an object tag?
You need to access the <object> contentDocument in order to access its contained elements.
To do this, you also need to wait until your <object> has loaded its content.
I'm not too much into d3, so it might not be the best d3 way of doing, but at least it works:
(but not in StackSnippet's null origined iframes...)
fetch('https://upload.wikimedia.org/wikipedia/commons/f/fd/Ghostscript_Tiger.svg')
.then(r => r.blob())
.then(b => obj.data = URL.createObjectURL(b));
obj.onload = function() { // wait for the svg has loaded
var tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("Hello");
var obj = this; // we're in the object's load event handler.
var svg = d3.select(obj.contentDocument) // get the contentDocument
.select("svg"); // then get the svg inside
svg.selectAll('g')
.on("mouseover", function() {
return tooltip.style("visibility", "visible");
})
.on("mousemove", function() {
var event = d3.event;
var offset = 20;
var topPosition = (event.pageY - offset) + "px";
var leftPosition = (event.pageX + offset) + "px";
return tooltip.style("top", topPosition).style("left", leftPosition);
})
.on("mouseout", function() {
return tooltip.style("visibility", "hidden");
});
};
<script src="https://d3js.org/d3.v4.min.js"></script>
<object id="obj" type="image/svg+xml"></object>
Fiddle
I have no experience with d3 but this might help.
You need to wait untill your window is fully loaded.
// object of my SVG
<object id="fridge" data={FridgePicture} ></object>
window.onload = function() {
// Get the fridgeObject by ID
var fridgeObj = document.getElementById("fridge");
// Get the SVG document inside the Object tag
var fridgeDoc = fridgeObj.contentDocument;
// From here you can get an item you wish e.g:
var rightDoor = fridgeDoc.getElementById("RightDoor");
};
Just a short summery which methods work and which won't:
d3.select(obj.node().contentDocument).select('svg').selectAll('g') // get the contentDocument
console.log('node > svg > g => works!',nsvg)
var obj1 = d3.select('#obj'); // we're in the object's load event handler.
var ng = d3.select(obj1.node().contentDocument).selectAll('g') // get the contentDocument
console.log('node > g => works!',ng)
var obj2 = d3.select('#obj'); // we're in the object's load event handler.
var svg = d3.select(obj2.contentDocument).select('svg').selectAll('g') // get the contentDocument
console.log('select svg > g => fails!',svg)
var obj3 = d3.select('#obj'); // we're in the object's load event handler.
var g = d3.select(obj3.contentDocument).selectAll('g') // get the contentDocument
console.log('select g => fails!',g)
var getElement = document.getElementById('obj').contentDocument
var gElements = d3.select(getElement).select('svg').selectAll('g')
console.log("gElements > d3.select svg > g => works!", gElements);
var svg2 = document.getElementById('obj').contentDocument.getElementsByTagName('g')
console.log("getElementById => works!", svg2);
};
https://jsfiddle.net/hirschferkel/0wu3z1st/11/

Switch for making background map from Mapbox visible/invisible in D3

I'm building a Voronoi map inspired by http://chriszetter.com/blog/2014/06/15/building-a-voronoi-map-with-d3-and-leaflet/. I'd like to make an option to turn off the background map as the location of the data may not be relevant in all my use cases. Furthermore, it would be great if the visualization could work offline this way. After toggling the switch, the entire background would be white. The Voronoi overlap would be the same. How do I do that? Here's the code (zip-file contains the csv-files): https://www.dropbox.com/s/i8vtfh8mkxazfr0/voronoi-maps-master.zip?dl=0
EDIT: There's two layer variables in the original code as I was trying to split the visualization into two. However, that attempt was unsuccessful and only mapLayer is used. This may not have been clear in the original question.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link href="base.css" rel="stylesheet" />
<link href='https://api.tiles.mapbox.com/mapbox.js/v1.6.3/mapbox.css' rel='stylesheet' />
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div id='map'>
</div>
<div id='loading'>
</div>
<!-- <div id='selected'>
<h1>...</h1>
</div> -->
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min.js"></script>
<script src="https://api.tiles.mapbox.com/mapbox.js/v1.6.3/mapbox.js"></script>
<!-- <script src="/voronoi-map/lib/voronoi_map.js"></script> -->
<script type="text/javascript" src="voronoi_map.js"></script>
<script>
map = L.mapbox.map('map', 'zetter.i73ka9hn') // <- dur ikke!
.fitBounds([[59.355596 , -9.052734], [49.894634 , 3.515625]]);
url = 'supermarkets.csv';
initialSelection = d3.set(['Tesco', 'Sainsburys']);
voronoiMap(map, url, initialSelection);
</script>
</body>
</html>
voronoi_map.js
voronoiMap = function(map, url, initialSelections) {
var pointTypes = d3.map(),
points = [],
lastSelectedPoint;
var voronoi = d3.geom.voronoi()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; });
var selectPoint = function() {
d3.selectAll('.selected').classed('selected', false);
var cell = d3.select(this),
point = cell.datum();
lastSelectedPoint = point;
cell.classed('selected', true);
d3.select('#selected h1')
.html('')
.append('a')
.text( /*point.name*/ "8 interactions from this cell")
/* .attr('href', point.url)
.attr('target', '_blank') */
}
var drawWithLoading = function(e){
d3.select('#loading').classed('visible', true);
if (e && e.type == 'viewreset') {
d3.select('#overlay').remove();
}
setTimeout(function(){
draw();
d3.select('#loading').classed('visible', false);
}, 0);
}
var draw = function() {
d3.select('#overlay').remove();
var bounds = map.getBounds(),
topLeft = map.latLngToLayerPoint(bounds.getNorthWest()),
bottomRight = map.latLngToLayerPoint(bounds.getSouthEast()),
existing = d3.set(),
drawLimit = bounds.pad(0.4);
filteredPoints = points.filter(function(d) {
var latlng = new L.LatLng(d.latitude, d.longitude);
if (!drawLimit.contains(latlng)) { return false };
var point = map.latLngToLayerPoint(latlng);
key = point.toString();
if (existing.has(key)) { return false };
existing.add(key);
d.x = point.x;
d.y = point.y;
return true;
});
voronoi(filteredPoints).forEach(function(d) { d.point.cell = d; });
var svg = d3.select(map.getPanes().overlayPane).append("svg")
.attr('id', 'overlay')
.attr("class", "leaflet-zoom-hide")
.style("width", map.getSize().x + 'px')
.style("height", map.getSize().y + 'px')
.style("margin-left", topLeft.x + "px")
.style("margin-top", topLeft.y + "px");
var g = svg.append("g")
.attr("transform", "translate(" + (-topLeft.x) + "," + (-topLeft.y) + ")");
var svgPoints = g.attr("class", "points")
.selectAll("g")
.data(filteredPoints)
.enter().append("g")
.attr("class", "point");
var buildPathFromPoint = function(point) {
return "M" + point.cell.join("L") + "Z";
}
svgPoints.append("path")
.attr("class", "point-cell")
.attr("d", buildPathFromPoint)
//.style('fill', function(d) { return '#' + d.color } )
.on('click', selectPoint)
.classed("selected", function(d) { return lastSelectedPoint == d} );
/* svgPoints.append("circle")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.style('fill', function(d) { return '#' + d.color } )
.attr("r", 2); */
}
/* function interactionGradient() {
color =
if
return
} */
var mapLayer = {
onAdd: function(map) {
map.on('viewreset moveend', drawWithLoading);
drawWithLoading();
}
};
var voronoiLayer = {
onAdd: function(map) {
map.on('viewreset moveend', drawWithLoading);
drawWithLoading();
}
};
map.on('ready', function() {
d3.csv(url, function(csv) {
points = csv;
points.forEach(function(point) {
pointTypes.set(point.type, {type: point.type, color: point.color});
})
map.addLayer(mapLayer);
})
});
}
You need to load the tileLayer seperately so you can create a reference to it which you can then in turn use to create a layercontrol which can easily enable/disable layers:
L.mapbox.accessToken = 'pk.eyJ1IjoicGF1bC12ZXJob2V2ZW4iLCJhIjoiZ1BtMWhPSSJ9.wQGnLyl1DiuIQuS0U_PtiQ';
// Create the tileLayer.
var tileLayer = L.mapbox.tileLayer('examples.map-i86nkdio');
var map = L.mapbox.map('mapbox', null, { // Do not add as parameter
'center': [0, 0],
'zoom': 1,
// Add here so it still gets added to the map initially
// You could skip this so it won't be added and you can
// turn it on via the layercontrol
'layers': [tileLayer]
});
// Create layer control
var layerControl = L.control.layers(null, {
'Tilelayer': tileLayer // Add tile layer to overlays
}).addTo(map);
Here's a working example on Plunker: http://plnkr.co/edit/5re3o6qnyCwAqXNYXrkP?p=preview
L.mapbox.tileLayer reference: https://www.mapbox.com/mapbox.js/api/v2.1.5/l-mapbox-tilelayer/
L.control.layers reference: http://leafletjs.com/reference.html#control-layers
Edit because of the comments:
You're already working with separate layers, the tilelayer (background) gets added upon map initalization L.mapbox.map('map', 'zetter.i73ka9hn') that in fact calls: L.mapbox.tileLayer('zetter.i73ka9hn').addTo(map). You'll need to do it that way because you'll need a reference to the layer so you can add it to L.control.layers like shown above. Your voronoi layer gets added in the voronoiMap method in the ready handler of the map: map.addLayer(mapLayer);
Thus as you can see they are already separated. Now if you also want to be able to toggle the voronoi layer in your layer control you'll need to add it to the layer control:
map.on('ready', function() {
d3.csv(url, function(csv) {
points = csv;
points.forEach(function(point) {
pointTypes.set(point.type, {
type: point.type,
color: point.color
});
});
map.addLayer(mapLayer);
layerControl.addOverlay(mapLayer, 'Voronoi'); // Here
})
});
But that in itself is not enough in your case because your layer doesn't have a onRemove method as prescribed by the ILayer interface:
http://leafletjs.com/reference.html#ilayer
Now if we add a onRemove method to your layer like this:
var mapLayer = {
onAdd: function(map) {
map.on('viewreset moveend', drawWithLoading);
drawWithLoading();
},
onRemove: function (map) {
d3.select('#overlay').remove();
}
};
It works: http://plnkr.co/edit/3z3pCAo0gGuA7xqnqiqb?p=preview (note i've commented out the ready handler because the map is ready before the function call so it wouldn't fire and changed some colors to make things more clear.) Hope this helps.

Nested for loops won't evaluate

I'm making an javascript image converter, just a proof of concept(I know there are another better languages for doing it). My problem is that in the loop section I'm doing some variables don't evaluate to the ones that are in the function loops. The code should work this way:
The HTML button activates the parent function.
Some variables are stated.
The loop that should make a nice gradient all over the and those I'm having trouble with are defined.
A function that triggers the loops in intervals is stated, with some other counter variables.e
I think the problem is in the counter variables I use on the loop() but I can't figure them out.
Here is all my code:
<!DOCTYPE html>
<html>
<head>
<html xmlns="http://www.w3.org/1999/xhtml">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CPBitmap to PNG</title>
<style>
#canvas {
width:512px;
height:384px;
background-color:black;
}
</style>
<script>
function convertir(){
var y =0;
vertC = 10;
var pixelConverter = function(){
for(;y<vertC;y++){
for(var x=0; x<2048;x++){
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var pixel = "rgb(" + y + "," + vertC + "," + x + ")";
ctx.fillStyle = pixel;
ctx.fillRect(x,y,100,100);
}
}
}
intervalos = setInterval(loops,1);
function loops(){
pixelConverter();
vertC = vertC + 10;
console.log("Y= " + y + "Linea " + vertC );
y = y + 10;
x = 0;
console.log(x);
if(x > 2047 && y > 1535)intervalos=window.clearInterval(intervalos);
else pixelConverter();
}
}
</script>
</head>
<body>
<input type="file" id="input" name="file" />
<button onclick=convertir() >¡Convertir!</button>
<button onclick="intervalos=window.clearInterval(intervalos)">Alto</button>
<canvas id="canvas" width="2048px" height="1536px"></canvas>
<div id="Hola"></div>
</body>
</html>

Categories