Trying to change a value of an object only for the single instance.
Im trying to make a small library which can a few of the things that jQuery does, just in order to learn the tricks.
I currently have a problem with keeping my elements in the object for usage.
Working example:
Brain('#test').html('a'); // this will change the innerHTML of #test with "a".
Not working example:
var a = Brain('#test');
var b = Brain('#test2');
a.html('a'); // should change content of #test to "a", but changes content of #test2 to "a"
My JS:
// create the brain function to select elements
var Brain = function(selector, context)
{
if (typeof context == 'undefined')
var context = document;
Brain.elements = context.querySelector(selector);
return Brain;
};
// set and get innerHTML of element
Brain.html = function(content)
{
if (typeof content != 'undefined')
Brain.elements.innerHTML = content;
return Brain.elements.innerHTML;
};
// testing
var a = Brain('#test');
var b = Brain('#test2');
a.html('a');
b.html('b');
My HTML
<p id="test">test</p>
<p id="test2">test2</p>
jsfiddle example
Hi i Just gone through your code. the loop is rotating twice and ends at #test2 id. that means Brain contains element of #test2 that's why second element is changing.
Change code as below it is working
var a = Brain('#test').html('a');
var b = Brain('#test2').html('b');
Happy codeing
Related
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
})
...
}
The following code checks if the selected tag has childnodes. If a child node is present , it loops till a child node is found. When there are no further child nodes found, it loops out i.e it reaches a text node causing the loop to end. The function is made recursive to run until no child node is found. The code runs as per above info, but when I try to match TEXT_NODE (console.log() outputs all text node), replace() is used to identify phone numbers using regex and replaced with hyperlink. The number gets detected and is enclosed with a hyperlink but it gets displayed twice i.e. number enclosed with hyperlink and only the number.Following is the code
function DOMwalker(obj){
var regex = /\+\d{1,4}?[-.\s]?\(?\d{1,3}?\)?[-.\s]?\d{1,4}[-.\s]?\d{1,4}[-.\s]?\d{1,9}/g;
var y = "$&";
if(obj.hasChildNodes()){
var child = obj.firstChild;
while(child){
if(child.nodeType!==3)
{
DOMwalker(child);
}
if (child.nodeType=== 3) {
var text = child.nodeValue;
console.log(typeof text);
var regs = regex.exec(text);
match = text.replace(regex,y);
if(match){
var item = document.createElement('a');
item.setAttribute('href','javascript:void(0);');
var detect = document.createTextNode(match);
var x=item.appendChild(detect);
console.log(x);
child.parentNode.insertBefore(x,child);
}
}
child=child.nextSibling;
}
}
};
$(window).load(function(){
var tag = document.querySelector(".gcdMainDiv div.contentDiv");
DOMwalker(tag);
});
Following are the screenshot of the output:
Here the number gets printed twice instead of one with hyperlink which is been displayed(expected highlighted number with hyperlink) and second widout tags
Following is console.log of x
I have already gone through this.
The solution provided below works well with FF. The problem arises when used in IE11. It throws Unknown runtime error and references the .innerHTML. I used the appenChild(),but the error couldn't be resolved.
You've got a couple of problems with what you posted. First, if a child is not node type 3 and not a SCRIPT node, you re-call recursivetree() but you do not pass the child in. The function will just start over at the first div element and again, infinitely loop.
Second, you're calling replace() on the node itself, and not the node's innerHTML. You're trying to replace a node with a string, which just won't work, and I think you mean to replace any matching numbers within that node, rather than the entire node.
If you have <div>My number is +111-555-9999</div>, you only want to replace the number and not lose everything else.
Try this as a solution:
function recursivetree(obj){
var regex = /\+\d{1,4}?[-.\s]?\(?\d{1,3}?\)?[-.\s]?\d{1,4}[-.\s]?\d{1,4}[-.\s]?\d{1,9}/g;
var y = "$&";
var obj = obj || document.getElementsByTagName('div')[0];
if(obj.hasChildNodes()){
var child = obj.firstChild;
while(child){
if(child.nodeType !== 3 && child.nodeName !== 'SCRIPT'){
//Recall recursivetree with the child
recursivetree(child);
}
//A nodeType of 3, text nodes, sometimes do not have innerHTML to replace
//Check if the child has innerHTML and replace with the regex
if (child.innerHTML !== undefined) {
child.innerHTML = child.innerHTML.replace(regex,y);
}
child=child.nextSibling;
}
}
}
recursivetree();
Fiddle: http://jsfiddle.net/q07n5mz7/
Honestly? If you're trying to loop through the entire page and replace all instances of numbers, just do a replace on the body.
var regex = /\+\d{1,4}?[-.\s]?\(?\d{1,3}?\)?[-.\s]?\d{1,4}[-.\s]?\d{1,4}[-.\s]?\d{1,9}/g;
var y = "$&";
var body = document.body;
body.innerHTML = body.innerHTML.replace(regex, y);
Fiddle: http://jsfiddle.net/hmdv7adu/
Finally, I got the solution of my question. I referred to this answer which helped me to solve my query.
Here goes the code:
function DOMwalker(obj){
if(obj.hasChildNodes()){
var child = obj.firstChild;
var children = obj.childNodes;
var length = children.length;
for(var i = 0;i<length;i++){
var nodes = children[i];
if(nodes.nodeType !==3){
DOMwalker(nodes);
}
if(nodes.nodeType===3){
//Pass the parameters nodes:current node being traversed;obj:selector being passed as parameter for DOMwalker function
highlight(nodes,obj);
}
}
child = child.nextSibling;
}
}
function highlight(node,parent){
var regex =/(\d{1}-\d{1,4}-\d{1,5})|(\+\d{1,4}?[-.\s]?\(?\d{1,3}?\)?[-.\s]?\d{1,4}[-.\s]?\d{1,4}[-.\s]?\d{1,9})/g;
//Stores the value of current node which is passed through the if loop
var matchs = node.data.match(regex);
if matchs is true,add it to DOM
if(matchs){
var anchor = document.createElement("a");
var y = /[(]\d[)]|[.-\s]/g;//removes spaces periods or dash,also number within brackets
var remove = number.replace(y,'');
//tel uri,if you have an app like skype for click-to dial
anchor.setAttribute("href","tel:"+remove);
//the anchor tag should be inserted before in the current node in the DOM
parent.insertBefore(anchor,node);
//append it toh the DOM to be displaye don the web page
anchor.appendChild(node);
}
else
{
return false;
}
}
I hope this code helps others.
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
I am creating an application that uses the same number pad to fill out two separate text style form values using javascript.
I found out how to gather a div ID for use inside of a function (for say toggling the hide value), but I need to save this value somehow so that I can know which field to put the numbers into when they come in.
I tried using a global variable for this, but it does not seem to work as the ID does not seem to be recorded as a String value.
The code that I am using does toggle the show/hide attribute, but if I use an alert box to pop what the variable I am using as storage is it reads [object HTMLDivElement]
My script looks like this (bear in mind that I am a noob to javascript).
<script type="text/javascript">
<!--
keypad.display="none";
//Classes for the numberpad on the text fields.
var padName = ""; //Storage for the name of the current pad.
function numPad(field) {
var pad = document.getElementById("keypad"); //manipulating pad.
var ref = document.getElementById(field);//gather the field info.
if (pad.style.display == "block") { //Open or close?
pad.style.display = "none"; //Blank out.
padName = "";
}
else {
pad.style.display = "block";//Set to refer to correct field.
padname = ref;
alert (ref);
}
}
function click(id) {
var key = document.getElementById(id);
var total = padName.value();
if (key == "Backspace") total.slice(0, -1);
else if (key == "Enter") numPad("blanck");
else total += key;
padName.value = total;
}
-->
</script>
// to get the ID by direct property access of the DOM element
var ref = document.getElementById(field).id;
and then ref stores the ID value.
I would suggest:
// create an object to store app-wide settings
// access properties like this: appSettings.propertyName
var appSettings = { padName: "" };
...
var ref = document.getElementById(field).id;
appSettings.padName = ref;
to avoid polluting the global namespace.
To get/set the value of the pad, you'll need to do this:
// to get
var total = document.getElementById(appSettings.padName).value;
// to set
document.getElementById(appSettings.padName).value = "something";
You should read up on DOM objects and properties.
For starters, ref is assigned a reference to a DOM element. You are then assigning this reference to padName, hence the [object HTMLDivElement] alert.
If you just want the ID stored in padName, use
padName = field;
Also, you're mixing cases of padName. You have both padName and padname.
Further, as mentioned in the comments, use the console for debugging. It's much more comprehensive than an alert.
I can't tell what's happening in your click function. You seem to be expecting padName to be an object of some kind however where the value() method and value property comes from is anyone's guess (FYI only form elements have value properties).
I wonder what's the effectiviest way of creating an element with id='id' class='class' attr='attr' .. and appending it to another element and have it available as a variable?
So far I can do it with one line of code. But it requires a new set of javascript framework. I just want to make sure that there is no effectivier way first.
My solution is to create a javascript framework, which got the following syntax:
var variable = elementTagAppend/Prepend/After/Before('JQuerySelector', elementToAppend/Prepend/After/BeforeTo);
For instance, if I want to create an div element with id='id', class='class', attr='attr' and append it to an another element called "another"
var variable = divAppend('#id.class[attr = "attr"]', another); Very effective right?
Or if I want to prepend a form element with id='inputForm', class='inputForms':
var variable = formPrepend('#inputForm.inputForms', another);
var element = $("<div/>", {
id: "id",
class: "class",
attr: "attr"
}).appendTo("#element");
appendTo returns a JQuery object, you can read more on http://api.jquery.com/appendTo/
Edit: It looks like you're asking if an effective way to create elements is having methods for all HTML tags, so that your library doesn't have to do any sort of text parsing/regex.
This isn't a solution as it makes development a lot slower.
One way is to use a function and DOM methods:
<script type="text/javascript">
var elementData = {
tagName: 'div',
properties: {
id: 'div0',
onclick: function(){alert(this.id);},
className: 'someClass'
},
content: '<h1>New Div</h1>'
}
function elementBuilder(obj) {
var el = document.createElement(obj.tagName);
var props = obj.properties;
for (var p in props) {
if (props.hasOwnProperty(p)) {
el[p] = props[p];
}
}
if (typeof obj.content == 'string') {
el.innerHTML = obj.content;
}
return el;
}
</script>
<button onclick="
var newElement = elementBuilder(elementData);
document.body.insertBefore(newElement, this.nextSibling);
">Insert element</button>
The above could be expanded to create more than one element per call, I'll leave that you.
Another method is to take an HTML string, convert it to an element (or elements) and return it in a document fragment:
<script type="text/javascript">
var htmlString = '<p><b>paragraph</b></p>' +
'<p>Another p</p>';
function byInnerHTML(s) {
var d = document.createElement('div');
d.innerHTML = s;
var frag = document.createDocumentFragment();
while (d.firstChild) {
frag.appendChild(d.firstChild);
}
return frag;
}
</script>
<button onclick="
var newContent = byInnerHTML(htmlString);
document.body.insertBefore(newContent, this.nextSibling);
">Insert element</button>
The second one returns a document fragment that can be inserted into the page. Of course if you want to generate parts of a table, the second method isn't quite so good.
In tune with a pure JavaScript implementation, here's my take on the problem:
function createElement(type, attributes, parent){
var node = document.createElement(type);
for (i in attributes){
node.setAttribute(i, attributes[i])
}
if (parent && parent.__proto__.appendChild){
parent.appendChild(node)
}
return node;
}
And you use it like this:
createElement("span", { "id": "theID", "class": "theClass", "title": "theAttribute"}, document.getElementById("theParent"))
I kept one of your initial requirements: append the node to some other element and also return it.
I particularly like RobG's second example with the fragment. Definitely something I'd try if I were using plain strings to represent HTML.
If it doesn't warrant the use-case, it doesn't always make sense to import a JS framework. jQuery is no exception. Should you decide more complex workflows are needed, that's your warrant! ;)