How do I replace an SVG element - javascript

I have a svg document - map of the world, which resides in its own file. I am getting some data from the database which I then loop thru add xlink to the appropriate countries which are appended to the end of the document. However when this happens, the appended xlinks are drawn over other nodes. For example the country of South Africa has Lesotho within its boundaries. South Africa gets a link and then ends up covering Lesotho. How do I make sure Lesotho is accessible or redrawn after the link is created? This is the code I am using.
window.onload = function() {
// Get the Object by ID
var a = document.getElementById("SVGWorldMap");
// Get the SVG document inside the Object tag
var svgDoc = a.contentDocument;
// Get one of the SVG items by ID;
var svgItem = svgDoc.getElementById("ocean");
// Set the colour to something else
svgItem.style.fill = "#5593BB";
for (let i = 0; i < Countries.Countries.length; i++) {
var x = svgDoc.getElementById(Countries.Countries[i].locCountry.toLowerCase());
var link = svgDoc.createElementNS("http://www.w3.org/2000/svg", "a");
link.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', "../Scripts/prodList.aspx?idLocation=" + Countries.Countries[i].idLocation);
link.setAttribute('target', '_top');
svgDoc.documentElement.appendChild(link);
x.style.fill = "#123456";
x.addEventListener('mouseover', mouseOverEffect);
x.addEventListener('mouseout', mouseOutEffect);
link.appendChild(x);
//console.log(Countries.Countries[i].locCountry.toLowerCase());
}
};

Try preserving the order of the elements by replacing each one with a link instead of adding links to the end in Countries.Countries order (assuming the links and paths(?) share a parent).
So instead of this:
svgDoc.documentElement.appendChild(link);
do this:
x.parentNode.replaceChild(link, x);

Related

Google apps script - retain links when copying footnote content

Background
I have a Google Apps Script that we use to parse the footnote content, wrapped in double parenthesis, in place of the footnote number superscript. The intended result should be:
Before Script
This is my footie index.1 1This is my
footie content with a link and emphasis.
After Script
This is my footie index. (( This is my footie content with a
link and emphasis.)
Problem
Everything works fine, except when I parse the footnotes in double parenthesis, they are losing all the links and formatting:
This is my footie index. (( This is my footie content with a
link and emphasis.)
If anyone can assist me with fixing the code below I would really appreciate the help :)
SOLUTION:
function convertFootNotes () {
var doc = DocumentApp.getActiveDocument()
var copy = generateCopy(doc) // make a copy to avoid damaging the original
var openCopy = doc; //DocumentApp.openById(copy.getId()) // you have to use the App API to copy, but the Doc API to manipulate
performConversion(openCopy); // perform formatting on the copy
}
function performConversion (docu) {
var footnotes = docu.getFootnotes(); // get the footnotes
footnotes.forEach(function (note) {
// Traverse the child elements to get to the `Text` object
// and make a deep copy
var paragraph = note.getParent(); // get the paragraph
var noteIndex = paragraph.getChildIndex(note); // get the footnote's "child index"
insertFootnote(note.getFootnoteContents(),true, paragraph, noteIndex);
note.removeFromParent();
})
}
function insertFootnote(note, recurse, paragraph, noteIndex){
var numC = note.getNumChildren(); //find the # of children
paragraph.insertText(noteIndex," ((");
noteIndex++;
for (var i=0; i<numC; i++){
var C = note.getChild(i).getChild(0).copy();
if (i==0){
var temp = C.getText();
var char1 = temp[0];
var char2 = temp[1];
if (C.getText()[0]==" "){
C = C.deleteText(0,0);
}
}
if (i>0){
paragraph.insertText(noteIndex,"\n");
noteIndex++;
}
paragraph.insertText(noteIndex,C);
noteIndex++;
} //end of looping through children
paragraph.insertText(noteIndex,"))");
}
function generateCopy (doc) {
var name = doc.getName() + ' #PARSED_COPY' // rename copy for easy visibility in Drive
var id = doc.getId()
return DriveApp.getFileById(id).makeCopy(name)
}
Were there any changes to the code other than the added )) to make it not work? Removing the (( & )) still did not have the formatting applied when testing it; getText() returns the element contents as a String, not a rich text object/element which contains the formatting info.
To get to the Text object:
getFootnoteContents().getChild(0) returns the FootnoteSection Paragraph
getChild(0).getChild(0) returns the Text object of that paragraph
copy() returns a detached deep copy of the text object to work with
Note: If there are other child elements in the FootnoteSection or in it's Paragraph child, you'll want to add some kind of type/index checking to get the correct one. However, with basic footnotes - as the above example - this is the correct path.
function performConversion (docu) {
var footnotes = docu.getFootnotes() // get the footnotes
var noteText = footnotes.map(function (note) {
// Traverse the child elements to get to the `Text` object
// and make a deep copy
var note_text_obj = note.getFootnoteContents().getChild(0).getChild(0).copy();
// Add the `((` & `))` to the start and end of the text object
note_text_obj.insertText(0, " ((");
note_text_obj.appendText(")) ");
return note_text_obj // reformat text with parens and save in array
})
...
}

Load SVG from file to canvas and ungroup it

I upload an SVG file to a canvas using FabricJS with the function
fabric.loadSVGFromURL (url, function(objects, options){
group = fabric.util.groupSVGElements(objects, options);
canvas.add(group).centerObject(group).renderAll();
});
This works perfectly. However the next step I want do is to ungroup the recently added group. The reason why I need to ungroup is that I want to be able to select the group's child elements by clicking on them since there is no access to these elements if they are grouped.
I found a snippet to perform an ungroup however when I do it with the group created width groupSVGElements the elements lose their original position scrambling the whole svg that I loaded.
Does anyone knows how to ungroup a loaded SVG and still keep the original positions of the elements?
You can still access each of the element using perPixelTargetFind
When set to true, objects are "found" on canvas on per-pixel basis rather than according to
bounding box.
I'm looking for the same solution. Did you find an answer so far?
Looking at the structure of an SVG element, I would image it should be possible to write a recursive method, which gives the children, the properties of the group and places them one level up. If you keep doing this, you should end up with all groups exploded and all properties intact (which are inherited otherwise).
Looking at SVG-EDIT, there is a function which should do this:
Function: ungroupSelectedElement
// Unwraps all the elements in a selected group (g) element. This requires
// significant recalculations to apply group's transforms, etc to its children
this.ungroupSelectedElement = function() {
var g = selectedElements[0];
if (!g) {
return;
}
if ($(g).data('gsvg') || $(g).data('symbol')) {
// Is svg, so actually convert to group
convertToGroup(g);
return;
}
if (g.tagName === 'use') {
// Somehow doesn't have data set, so retrieve
var symbol = svgedit.utilities.getElem(getHref(g).substr(1));
$(g).data('symbol', symbol).data('ref', symbol);
convertToGroup(g);
return;
}
var parents_a = $(g).parents('a');
if (parents_a.length) {
g = parents_a[0];
}
// Look for parent "a"
if (g.tagName === 'g' || g.tagName === 'a') {
var batchCmd = new svgedit.history.BatchCommand('Ungroup Elements');
var cmd = pushGroupProperties(g, true);
if (cmd) {batchCmd.addSubCommand(cmd);}
var parent = g.parentNode;
var anchor = g.nextSibling;
var children = new Array(g.childNodes.length);
var i = 0;
while (g.firstChild) {
var elem = g.firstChild;
var oldNextSibling = elem.nextSibling;
var oldParent = elem.parentNode;
// Remove child title elements
if (elem.tagName === 'title') {
var nextSibling = elem.nextSibling;
batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(elem, nextSibling, oldParent));
oldParent.removeChild(elem);
continue;
}
children[i++] = elem = parent.insertBefore(elem, anchor);
batchCmd.addSubCommand(new svgedit.history.MoveElementCommand(elem, oldNextSibling, oldParent));
}
// remove the group from the selection
clearSelection();
// delete the group element (but make undo-able)
var gNextSibling = g.nextSibling;
g = parent.removeChild(g);
batchCmd.addSubCommand(new svgedit.history.RemoveElementCommand(g, gNextSibling, parent));
if (!batchCmd.isEmpty()) {addCommandToHistory(batchCmd);}
// update selection
addToSelection(children);
}
};
See also:
https://code.google.com/p/svg-edit/source/browse/trunk/editor/svgcanvas.js

How to change the URL of an image tag on every click on the image using JavaScript?

I have an element displaying an image on an HTML page. This element's source is one of many different images in a JavaScript array.
I already have a script for looping through the images, creating a slideshow effect, but now I want to manually flick through the images with buttons.
This is my code so far, but I get no response when clicking the button.
function nextup()
{
imgs = [];
imgs[0] = "/snakelane/assets/images/thumb/_1.jpg"; imgs[10] = "/snakelane/assets/images/thumb/_19.jpg";
imgs[1] = "/snakelane/assets/images/thumb/_2.jpg"; imgs[11] = "/snakelane/assets/images/thumb/_20.jpg";
imgs[2] = "/snakelane/assets/images/thumb/_3.jpg"; imgs[12] = "/snakelane/assets/images/thumb/_21.jpg";
imgs[3] = "/snakelane/assets/images/thumb/_4.jpg"; imgs[13] = "/snakelane/assets/images/thumb/_22.jpg";
imgs[4] = "/snakelane/assets/images/thumb/_5.jpg"; imgs[14] = "/snakelane/assets/images/thumb/_23.jpg";
imgs[5] = "/snakelane/assets/images/thumb/_6.jpg"; imgs[15] = "/snakelane/assets/images/thumb/_24.jpg";
imgs[6] = "/snakelane/assets/images/thumb/_7.jpg"; imgs[16] = "/snakelane/assets/images/thumb/_25.jpg";
imgs[7] = "/snakelane/assets/images/thumb/_8.jpg"; imgs[17] = "/snakelane/assets/images/thumb/_26.jpg";
imgs[8] = "/snakelane/assets/images/thumb/_9.jpg"; imgs[18] = "/snakelane/assets/images/thumb/_27.jpg";
imgs[9] = "/snakelane/assets/images/thumb/_32.jpg"; imgs[19] = "/snakelane/assets/images/thumb/_28.jpg";
var pic = document.getElementById("picbox");
for(i =0; i < imgs.length; i++) {
var current = indexOf(pic.src);
var next = Math.round(current + 1);
pic.src = imgs[next];
}
}
Can anyone tell me what's wrong with my code or suggest a better way?
Multiple problems in the approach you had used. Have a look at the modified function below. Let me know if you need explanation with anything.
The following code will use an array containing image URLs and later assign in a sequential manner to an img tag on click. Enjoy!
Here you can try to see the output.
function nextup(){
//Initialized img array with 10 images, you can do it any way you want to.
var imgs = [];
for(i=0;i<10;i++){
imgs[i] = "http://lorempixel.com/output/cats-q-c-100-100-"+(i+1)+".jpg";
}
//Fetch the pic DOM element by ID
var pic = document.getElementById("picbox");
//Know what is position of currently assigned image in array.
var current = imgs.indexOf(pic.src);
var next = 0;
//Handle case if no image is present, the initial case.
if(current!=-1){
next = (current + 1)%(imgs.length);
}
//Assign the next src
pic.src = imgs[next];
}
//Scoped outside to call the function first time on load.
nextup();
I found the following problems in your code:
You tried to use indexOf without specifying the array in which the search has to be performed. Imagine s school principal asking someone to go find if John is present in the classroom without specifying a specific classroom.
For iterating through array you used a next variable which could have been a good idea if you needed an endless loop. But here since we are limited to 10 or 20 images we need to make sure that if the currently selected image is the last one, we find that next goes to 21 (assuming a total of 20 images.) and this would try to access a variable out of bounds.
Hence I've used the mod operator %.
For reference in JavaScript, 5%10 would return 5 , 15%10 would return 5 and so on. Read more about the mod operator HERE.

Random element from an array of images and their respective links on page refresh

I've got an unordered list of images. I'd like for each of those images to have an href attribute (since I would like to use a lightbox-popup to display the correlating image when clicked), but for each image to be random when the page is refreshed.
In short, for each image coupled with its href to be random, but the order of the ul to stay the same. What would be a way of achieving this?
var img_array = []; //assuming your array is called this and full of porn pictures
//fisher-yates algorithm. Google it
(function(){
var tmp, current, top = img_array.length;
if(top) while(--top) {
current = Math.floor(Math.random() * (top + 1));
tmp = img_array[current];
img_array[current] = img_array[top];
img_array[top] = tmp;
}
})();
//target html element where the images are appended
var target = document.getElementById('someTargetDiv');
for(var i=0, len=img_array.length; i<len; i++){
var link = document.createElement('a'),
img = document.createElement('img');
link.setAttribute('href', img_array[i]);
img.setAttribute('src', img_array[i]);
link.appendChild(img);
target.appendChild(link);
}
This is not an optimized way of doing this kind of operation. I've made it slightly more verbose for your understanding.
The biggest mistake with this code is the constant creation of many different elements and appending them to the DOM one at a time. What should really be done is to build a string of generated html, and changing the innerHTML of a target div in one operation.
var html = [];
for(var i=0, len=img_array.length; i<len; i++){
html.push('<img src="'+ img_array[i] +'">');
}
target.innerHTML = html.join('');
Hopefully I get this right.
With jQuery you could do it like this jsfiddle:
$('ul').children('li').sort(function() {
return Math.round(Math.random()); // randomly get 0 or 1
}).appendTo('ul');
Doing it with PHP on server side would make more sense, but since you have no db resource or so, you'd have to make a static array of image:link pairs then shuffle it and generate a random list.
UPDATE
Maybe this comes closer to a solution:
jsfiddle
var contents = new Array(),
$list = $('li');
$list.each(function() { contents.push($(this).children()); });
contents.sort(function() {
return 0.5 - Math.random();
});
$list.each(function() {
$(this).append(contents.shift());
});

Can't put search results into Hyperlinks and append to a div

okay i have come across a simple javascript code that will search all the hyperlinks in a page which works briliantly fast. the only problem is that the browser freezes when it tries to remake these links in a div, no error just as soon as i push the button the browser refuses to load. as you can see it gets the input from a form then searches every hyperlink for these terms then is supposed to populate a div with links but it doesn't. the code is as follows.
function search0(){
var lists = document.getElementsByTagName("a");
for (var i = 0; i < lists.length; i++) {
var output = lists[i];
var team1 = document.getElementById("search1").value;
var matchPos1 = output.innerHTML.search(team1);
if(matchPos1 != -1){
var team2 = document.getElementById("search2").value;
var matchPos2 = output.innerHTML.search(team2);
if(matchPos2 != -1){
var elem1 = document.createElement("a")
var styleattr=document.createAttribute("href");
styleattr.nodeValue=output;
elem1.setAttributeNode(styleattr);
var text1 = document.createTextNode(output.innerhtml);
elem1.appendChild(text1);
var parentdiv = document.getElementById("frame2");
parentdiv.appendChild(elem1);
}
}
}
}
You are creating an infinite loop.
The nodeList you create with document.getElementsByTagName("a") is live i.e. if you add a link to the page it will appear in this list automatically! Yes, that's right, even without requerying. Here's a reference doc.
You are adding links to the nodeList which are then matched and added to the end on the nodeList which are then matched and so on and so on
To do what you want to do you should create an initial array of links like this.
//creates a real js array from a nodelist
var list = Array.prototype.slice.call( document.getElementsByTagName("a"), 0 );
Here is an explanation of Array.prototype.slice.call
Also change case-sensitive mistake:
var text1 = document.createTextNode(output.innerhtml);
To
var text1 = document.createTextNode(output.innerHTML);

Categories

Resources