How do you detect if an html element can append child nodes? - javascript

I created a custom jquery event called 'loading' in my application. I want to append a masking element with a spinner, when this event is triggered. I can figure out that part without problems. However, some elements (images, form inputs, etc..) cannot append child elements. I need to be able to detect if the target of this event can receive child elements. If it cannot, then I will add the spinner & mask to it's parent element.

You have to check the name:
/*global $, jQuery */
function isVoid(el) {
var tags = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input',
'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'],
i = 0,
l,
name;
if (el instanceof jQuery) {
el = el[0];
}
name = el.nodeName.toLowerCase();
for (i = 0, l = tags.length; i < l; i += 1) {
if (tags[i] === name) {
return true;
}
}
return false;
}
And use it like this:
var el = document.getElementById('el'),
elj = $('#el');
if (!isVoid(el)) {
// append
}

You can add child elements, they just won't render. This may sound like a semantic distinction, but it's critical to your problem: the DOM doesn't know whether a particular tag is rendered or not.
Your best bet is just to check manual:
var allowChildren = function(elem) {
return ! (elem.nodeName in { INPUT : true, IMG : true }) ;
};

http://jsfiddle.net/mblase75/eGyRH/3/ -- Chrome lets me add a child element to an <input> tag, even though it's not displayed at all. It's visible in the DOM debugger, just not in the browser window.
However, I can test it's .width() and see that's equal to zero or not: http://jsfiddle.net/mblase75/eGyRH/4/
alert($('.element').append('<span>asdf</span>').children().width());​
However, this will also be zero width if, for instance, I'm adding the span to a hidden div: http://jsfiddle.net/mblase75/eGyRH/5/ -- so it's not totally reliable either.

The accepted answer has a very nice fuction based on which types of nodes can be void. It can be replaced by a jQuery one-liner:
node.is("area,base,br,col,command,embed,hr,img,input,keygen,link,meta,param,source,track,wbr")

Related

How to use createElement() in JavaScript?

var htmlComponent = [
{
element : 'button',
text : "Addition"
},
{
element : 'h1',
text : "This is the heading"
},
{
element : 'p',
text : "This is the paragraph."
}
];
htmlComponent.forEach(function(item) {
// Problem here
document.body.appendChild(document.createElement(item.element).appendChild(document.createTextNode(item.text)));
}
Actually I wanted to create an html element using DOM Object but this is not working. I mean my code is not working properly..
but when I changed something Like that:
htmlComponent.forEach(function(item) {
var _element = document.createElement(item.element);
var text = document.createTextNode(item.text);
_element.appendChild(text);
document.body.appendChild(_element);
}
Then the code is working.
Here the main question is why 2nd code is working and the 1st one is not working...... what is the problem in my code.
please Explain me........
You are chaining the calls together like body.createElement().appendChild() where you shouldn't.
This works with createElement() because it returns the element you want to append to, but it doesn't work with appendChild() because that returns the child you just appended, which you are then appending again to the body.
This programming style is known as a "fluent" interface. It is supported by some libraries e.g. jQuery, but not by native Javascript DOM functions.
See https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild
Try as follows
appendChild does not return parent
var htmlComponent = [{
element: 'button',
text: "Addition"
},
{
element: 'h1',
text: "This is the heading"
},
{
element: 'p',
text: "This is the paragraph."
}
];
htmlComponent.forEach(function(element) {
var btn = document.createElement(element.element);
var t = document.createTextNode(element.text);
btn.appendChild(t);
document.body.appendChild(btn);
});
According to the documentation for appendChild:
The returned value is the appended child except when the given child
is a DocumentFragment, in which case the empty DocumentFragment is
returned.
You are appending a text node to the button, then trying to append the returned result to the body. This is the reason why you are not seeing the button being appended to the body.
If you break it down like this, it's easier to see what's going on:
document.body.appendChild(
// createElement returns button
document.createElement("button")
// button.appendChild then returns the appended child (a text node)
.appendChild(document.createTextNode("text"))
)

How to add a new Inspector(apart from the Inspector of elements and links) in jointJS - Rappid

I want to add a 3rd Inspector which will open only for an element(not a link) of specific type, for example only for basic.Rect in Rappid.
So far, there are 2 Inspectors.For elements and for links.
Is there any way it can be done?
The following code is a part of the KitchenSkink version of Rappid.
Here is function createInspector:
createInspector: function(cellView) {
var cell = cellView.model || cellView;
// No need to re-render inspector if the cellView didn't change.
if (!this.inspector || this.inspector.options.cell !== cell) {
// Is there an inspector that has not been removed yet.
// Note that an inspector can be also removed when the underlying cell is removed.
if (this.inspector && this.inspector.el.parentNode) {
this.inspectorClosedGroups[this.inspector.options.cell.id] = _.map(app.inspector.$('.group.closed'), function(g) {
return $(g).attr('data-name');
});
// Clean up the old inspector if there was one.
this.inspector.updateCell();
this.inspector.remove();
}
var inspectorDefs = InspectorDefs[cell.get('type')];
this.inspector = new joint.ui.Inspector({
inputs: inspectorDefs ? inspectorDefs.inputs : CommonInspectorInputs,
groups: inspectorDefs ? inspectorDefs.groups : CommonInspectorGroups,
cell: cell
});
this.initializeInspectorTooltips();
this.inspector.render();
$('.inspector-container').html(this.inspector.el);
if (this.inspectorClosedGroups[cell.id]) {
_.each(this.inspectorClosedGroups[cell.id], this.inspector.closeGroup, this.inspector);
} else {
this.inspector.$('.group:not(:first-child)').addClass('closed');
}
}
}
If you use joint.ui.Inspector.create('#path', inspectorProperties) any previous instance of the Inspector in a specific DOM element is removed and new one is created and rendered into the DOM automatically (it avoids creating a new instance of joint.ui.Inspector(), rendering it, adding the rendered result manually and removing the previous instance).
It also keeps track on open/closed groups and restore them based on the last used state.
Besides this, you may always have several different inspectorProperties objects previously defined when you are about to create() the inspector. So following the code you pasted, you could perform the tests you need first and then create the appropriate inspector:
if(cell instanceof joint.basic.Rect){
var customInputs = _.clone(CommonInspectorInputs);
// extend more inputs into `customInputs` from a variable previously defined
// OR modify the default rectangle's inspector directly, example:
customInputs.attrs.text = {
type: 'textarea',
label: 'Multiline text',
text: 'Type\nhere!',
group: joint.util.getByPath(CommonInspectorInputs.attrs, 'text/group', '/');
};
joint.ui.Inspector.create('.extra-inspector-container', {
cell: cell
inputs: customInputs,
groups: CommonInspectorGroups,
});
} // if only ONE inspector needs to be loaded add an ELSE block here
// and use '.inspector-container' in the `create()` above
// If `InspectorDefs` is a global variable with all the cells inspectors properties
// create and load the default inspector
joint.ui.Inspector.create('.inspector-container', _.extend({cell: cell},
InspectorDefs[cell.get('type')])
);

jQuery slideDown not working on element with dynamically assigned id

EDIT: I cleaned up the code a bit and narrowed down the problem.
So I'm working on a Wordpress site, and I'm trying to incorporate drop-downs into my menu on mobile, which means I have to use jQuery to assign classes and id's to my already existing elements. I have this code that already works on premade HTML, but fails on dynamically created id's.
Here is the code:
...
var menuCount = 0;
var contentCount = 0;
//find the mobile menu items
var submenus = $('[title="submenu"]');
if (submenus.length && submenus.parent('.fusion-mobile-nav-item')) {
console.log(submenus);
submenus.addClass('dropdown-title').append('<i id="dropdown-angle" class="fa fa-angle-down" aria-hidden="true"></i>');
submenus.each(function() {
$(this).attr("href", "#m" + menuCount++);
})
var content = submenus.parent().find('ul.sub-menu');
content.addClass('dropdown-content');
content.each(function() {
$(this).attr("id", "m" + contentCount++);
})
}
$(document).on('click', '.dropdown-title', function(e) {
var currentAttrValue = $(this).attr('href');
if ($(e.target).is('.d-active') || $(e.target).parent('.dropdown-title').is('.d-active')) {
$(this).removeClass('d-active');
$(currentAttrValue).slideUp(300).removeClass('d-open');
} else {
$('.dropdown-title').removeClass('d-active');
$('.dropdown-content').slideUp(300).removeClass('d-open');
$(this).addClass('d-active');
console.log($(currentAttrValue));
//THIS LINE FAILS
$(currentAttrValue).slideDown(300).addClass('d-open');
}
e.preventDefault();
});
I've registered the elements with the class dropdown-title using $(document).on(...) but I can't figure out what I need to do to register the elements with the custom ID's. I've tried putting the event callback inside the .each functions, I've tried making custom events to trigger, but none of them will get the 2nd to last line of code to trigger. There's no errors in the console, and when I console log the selector I get this:
[ul#m0.sub-menu.dropdown-content, context: document, selector: "#m0"]
0
:
ul#m0.sub-menu.dropdown-content
context
:
document
length
:
1
selector
:
"#m0"
proto
:
Object[0]
So jQuery knows the element is there, I just can't figure out how to register it...or maybe it's something I'm not thinking of, I don't know.
If you are creating your elements dynamically, you should be assigning the .on 'click' after creating those elements. Just declare the 'on click' callback code you posted after adding the ids and classes instead of when the page loads, so it gets attached to the elements with .dropdown-title class.
Check this jsFiddle: https://jsfiddle.net/6zayouxc/
EDIT: Your edited JS code works... There also might be some problem with your HTML or CSS, are you hiding your submenus? Make sure you are not making them transparent.
You're trying to call a function for a attribute, instead of the element. You probably want $(this).slideDown(300).addClass('d-active'); (also then you don't need $(this).addClass('d-active'); before)
Inside submenus.each loop add your callback listener.
As you are adding the class dropdown-title dynamically, it was not available at dom loading time, that is why event listener was not attached with those elemnts.
var menuCount = 0;
var contentCount = 0;
//find the mobile menu items
var submenus = $('[title="submenu"]');
if (submenus.length && submenus.parent('.fusion-mobile-nav-item')) {
console.log(submenus);
submenus.addClass('dropdown-title').append('<i id="dropdown-angle" class="fa fa-angle-down" aria-hidden="true"></i>');
submenus.each(function() {
$(this).attr("href", "#m" + menuCount++);
// add callback here
$(this).click( function(e) {
var currentAttrValue = $(this).attr('href');
if ($(e.target).is('.d-active') || $(e.target).parent('.dropdown-title').is('.d-active')) {
$(this).removeClass('d-active');
$(currentAttrValue).slideUp(300).removeClass('d-open');
} else {
$('.dropdown-title').removeClass('d-active');
$('.dropdown-content').slideUp(300).removeClass('d-open');
$(this).addClass('d-active');
console.log($(currentAttrValue));
$(currentAttrValue).slideDown(300).addClass('d-active');
}
e.preventDefault();
});
})
var content = submenus.parent().find('ul.sub-menu');
content.addClass('dropdown-content');
content.each(function() {
$(this).attr("id", "m" + contentCount++);
})
}
Turns out my problem is that jQuery is adding to both the mobile menu and the desktop menu, where the desktop menu is being loaded first when I search for that ID that's the one that jQuery finds. So it turns out I was completely wrong about my suspicions.

Changing / writing to element using casperjs and capture screenshot after the change

Is it possible to change / add style to an element / DOM using casperjs ?
I would like to highlight the element before I capture a screenshot
I've tried doing the following:
1.Change the element - just adding border - this part works.
$(":contains('something')").filter(function() {
return (
$(this).clone() //clone the element
.children() //select all the children
.remove() //remove all the children
.end() //again go back to selected element
.filter(":contains('something')").length > 0)
}).css('border', 'solid 1px black');
2.Then capture a screenshot after the change.
this.capture('test.png', undefined, {
format: 'jpg',
quality: 75
});
Screenshot is taken but without the changes I've made on the element.
I would do it that way :
First here a function which returns the css property of an element
//return the css property of an element called by its selector
//getElementBounds() not sufficient
casper.css = function(selector,propertyName){
"use strict";
var css = this.evaluate(function(sel,prop) {
return $(sel).css(prop);
},selector,propertyName);
return css;
};
And after i would use waitFor() :
casper.thenEvaluate(function(){
//here your jQuery code
})
.waitFor(function check(){
//wait this code to be true, so when one of your element has been modified
return (casper.css("your selector","border")==="solid 1px black");
//then execute this function asking for the capture
},function then(){
this.capture('test.png');
}
//if waitFor never becomes true, that means your jQuery code or my function doesn't work
},function timeout(){
casper.test.fail("Fail to modify elements");
});
I just give you my idea, the code doesn't work, or it might be if you specify the selector of one element which will be modified.

Can someone explain the following javascript code?

In addition to the explanation, what does the $ mean in javascript? Here is the code:
var ZebraTable = {
bgcolor: '',
classname: '',
stripe: function(el) {
if (!$(el)) return;
var rows = $(el).getElementsByTagName('tr');
for (var i=1,len=rows.length;i<len;i++) {
if (i % 2 == 0) rows[i].className = 'alt';
Event.add(rows[i],'mouseover',function() {
ZebraTable.mouseover(this); });
Event.add(rows[i],'mouseout',function() { ZebraTable.mouseout(this); });
}
},
mouseover: function(row) {
this.bgcolor = row.style.backgroundColor;
this.classname = row.className;
addClassName(row,'over');
},
mouseout: function(row) {
removeClassName(row,'over');
addClassName(row,this.classname);
row.style.backgroundColor = this.bgcolor;
}
}
window.onload = function() {
ZebraTable.stripe('mytable');
}
Here is a link to where I got the code and you can view a demo on the page. It does not appear to be using any framework. I was actually going through a JQuery tutorial that took this code and used JQuery on it to do the table striping. Here is the link:
http://v3.thewatchmakerproject.com/journal/309/stripe-your-tables-the-oo-way
Can someone explain the following
javascript code?
//Shorthand for document.getElementById
function $(id) {
return document.getElementById(id);
}
var ZebraTable = {
bgcolor: '',
classname: '',
stripe: function(el) {
//if the el cannot be found, return
if (!$(el)) return;
//get all the <tr> elements of the table
var rows = $(el).getElementsByTagName('tr');
//for each <tr> element
for (var i=1,len=rows.length;i<len;i++) {
//for every second row, set the className of the <tr> element to 'alt'
if (i % 2 == 0) rows[i].className = 'alt';
//add a mouseOver event to change the row className when rolling over the <tr> element
Event.add(rows[i],'mouseover',function() {
ZebraTable.mouseover(this);
});
//add a mouseOut event to revert the row className when rolling out of the <tr> element
Event.add(rows[i],'mouseout',function() {
ZebraTable.mouseout(this);
});
}
},
//the <tr> mouse over function
mouseover: function(row) {
//save the row's old background color in the ZebraTable.bgcolor variable
this.bgcolor = row.style.backgroundColor;
//save the row's className in the ZebraTable.classname variable
this.classname = row.className;
//add the 'over' class to the className property
//addClassName is some other function that handles this
addClassName(row,'over');
},
mouseout: function(row) {
//remove the 'over' class form the className of the row
removeClassName(row,'over');
//add the previous className that was stored in the ZebraTable.classname variable
addClassName(row,this.classname);
//set the background color back to the value that was stored in the ZebraTable.bgcolor variable
row.style.backgroundColor = this.bgcolor;
}
}
window.onload = function() {
//once the page is loaded, "stripe" the "mytable" element
ZebraTable.stripe('mytable');
}
The $ doesn't mean anything in Javascript, but it's a valid function name and several libraries use it as their all-encompassing function, for example Prototype and jQuery
From the example you linked to:
function $() {
var elements = new Array();
for (var i=0;i<arguments.length;i++) {
var element = arguments[i];
if (typeof element == 'string') element = document.getElementById(element);
if (arguments.length == 1) return element;
elements.push(element);
}
return elements;
}
The $ function is searching for elements by their id attribute.
This function loops through the rows in a table and does two things.
1) sets up alternating row style. if (i % 2 == 0) rows[i].className = 'alt' means every other row has its classname set to alt.
2) Attaches a mouseover and mouseout event to the row so the row changes background color when the user mouses over it.
the $ is a function set up by various javascript frameworks ( such as jquery) that simply calls document.getElementById
The code basically sets alternating table rows to have a different CSS class, and adds a mouseover and mouseout event change to a third css class, highlighting the row under the mouse.
I'm not sure if jQuery, prototype or maybe another third party JS library is referenced, but the dollar sign is used by jQuery as a selector. In this case, the user is testing to see if the object is null.
$ is the so-called "dollar function", used in a number of JavaScript frameworks to find an element and/or "wrap" it so that it can be used with framework functions and classes. I don't recognize the other functions used, so I can't tell you exactly which framework this is using, but my first guess would be Prototype or Dojo. (It certainly isn't jQuery.)
The code creates a ZebraTable "object" in Javascript, which stripes a table row by row in Javascript.
It has a couple of member functions of note:
stripe(el) - you pass in an element el, which is assumed to be a table. It gets all <tr> tags within the table (getElementsByTagName), then loops through them, assigning the class name "alt" to alternating rows. It also adds event handlers for mouse over and mouse out.
mouseover(row) - The "mouse over" event handler for a row, which stores the old class and background colour for the row, then assigns it the class name "over"
mouseout(row) - The reverse of mouseover, restores the old class name and background colour.
The $ is a function which returns an element given either the elements name or the element itself. It returns null if its parameters are invalid (non-existent element, for example)
I believe the framework being used is Prototype, so you can check out their docs for more info
Have a look at the bottom of the article that you have got the code from, you'll see that they say you'll also need prototype's $ function. From article
In your CSS you’ll need to specify a
default style for table rows, plus
tr.alt and tr.over classes. Here’s a
simple demo, which also includes the
other functions you’ll need (an Event
registration object and Prototype’s $
function).

Categories

Resources