I have two click events that are almost identical, I am trying to refactor to make it more DRY. I want share some lines of code in a handlerShared() function - but I loose the context of $(this) which should be the DOM element that is clicked. There are two different elements an svg and button. They have some specific functionality but alot of it is the same. I guess what I want is a way to make $(this) select the specific DOM element.
function handelerShared() {
}
function handlerTabs() {
var $this = $(this)
var dataKey = $this.attr("data-key");
pushHistory(dataKey);
removeActiveClass();
hideContent();
$(this).addClass(activeClass);
var activeTab = $(this).attr('href');
$(activeTab).fadeIn();
return false;
}
function handlerDiagram() {
var $this = $(this)
var dataKey = $this.attr("data-key")
pushHistory(dataKey);
removeActiveClass();
hideContent();
scrollToTabs();
$(themeTab + "." + dataKey).addClass(activeClass);
var activeSVGTab = $(themeContent + "." + dataKey)
$(activeSVGTab).fadeIn();
return false;
}
I m try to take these lines of of each handler and out into a handlerShared()
var $this = $(this)
var dataKey = $this.attr("data-key");
pushHistory(dataKey);
removeActiveClass();
hideContent();
the clicks
$(themeTab).on( "click", handlerTabs );
$(themeDiagram).on( "click", handlerDiagram );
I believe you can just pass the element around:
function handlerShared(element) {
var $this = $(element);
var dataKey = $this.attr("data-key");
pushHistory(dataKey);
removeActiveClass();
hideContent();
}
function handlerTabs() {
handlerShared(this);
$(this).addClass(activeClass);
var activeTab = $(this).attr('href');
$(activeTab).fadeIn();
return false;
}
function handlerDiagram() {
handlerShared(this);
scrollToTabs();
$(themeTab + "." + dataKey).addClass(activeClass);
var activeSVGTab = $(themeContent + "." + dataKey)
$(activeSVGTab).fadeIn();
return false;
}
$(themeTab).on("click", handlerTabs);
$(themeDiagram).on("click", handlerDiagram);
I have a problem with jQuery. I want to write a function that dynamically replaces some content in my HTML.
This is the function:
function renderPage(datas, html) {
$(html).find("*").each(function(){
if ($(this).attr("data-func")) {
var df = $(this).attr("data-func");
var content = null;
eval("content = " + df + "($(this),datas);");
console.log(content);
}
});
}
The problem is, that it has no effect! I see in the log that the content variable is right, but the output is not different.
My function in eval:
function fill(element,data) {
element.html("BASSZA MEG");
var events = data.events;
var i = 0;
var n = events.length;
for (;i<n; i++) {
obj = events[i];
var tr = $("<tr>");
var nev = $("<td>");
var link = $("<a href='programbelso.html#id="+obj["event_id"]+"&logo="+obj["event_logo"]+"'>");
link.html(obj["event_name"]);
nev.append(link);
tr.append(nev);
element.append(tr);
}
console.log("element: " + element);
return element;
}
Firstly, do NOT loop over every single element in the document, and then use an if() statement. You can drastically improve the performance by using the right selector.
The following sample should achieve what you're trying to do without needing to use the evil eval() function, too:
function renderPage(datas, html) {
$('[data-func]').each(function() {
var df = $(this).attr("data-func");
var the_func = window[$(this).attr("data-func")];
if(typeof the_func === 'function') {
var content = the_func($(this), datas);
console.log(content);
}
});
}
Greetings fellow stackoverflowers,
I'm working on a jQuery class where I have a function that appends some html code to a container.
this.displayOptions = function(property, container) {
for (var i = 0; i < this[property + 'Count']; i++) {
$(container).append('');
}
}
So far, no problem.
In this same class I have another function
this.setProperty = function(property, index) {
this[property] = index;
this.update();
}
I want the appended HTML to call this function setProperty function when I click on it.
How can I do this?
For what's it worth, I initialize my class like this
var myVariable = new MyPlugin();
I know I could do <a href="#" onclick="myVariable.setProperty('', '')"> but the plugin can't know the variable.
Any help is appreciated!
Found my solution. I got it working like this:
this.displayOptions = function(property, container) {
var self = this;
for (var i = 0; i < this[property + 'Count']; i++) {
var element = $('<a href="" data-property="' + property + '" />');
$(container).append(element);
element.click( function() {
self.setProperty($(this).attr('data-property'), $(this).index());
return false;
});
}
}
I have a fiddle here: my fiddle. What I am trying to do is create a list of items from a separate group of lists. I cannot seem to get a grasp on what I am doing wrong, but here is whats happening:
I have a group of lists based on tabular data
Each list has the name of the column and a selection checkbox
If I select an item, it needs to be added to the selected columns area (vertical list)
There are 14 unique tabular items with checkboxes
(PROBLEM -->) When I select an item, it gets added 14 times in the selected columns section
code
(html):
I tried ti insert HTML but is not working right. Please look at the fiddle listed above.
(jquery):
var dte = // drag table elements
{
init: function() {
var chkbx = $('.group input[type="checkbox"]:checkbox');
//var chkbx = $('#accordion');
for (var i = 0, ii = chkbx.length; i < ii; i++) {
$(chkbx).bind("click", dte.adjustList);
}
},
adjustList: function(event) {
var list = [];
var str = '';
var eleval = event.currentTarget.value;
var eleid = event.currentTarget.id;
if (eleval == 1) {
list.push(eleid);
str = '<li>' + eleid + '</li>';
}
$('#vertical ul').append(str);
/*
//var ele = event.currentTarget.id;
var allVals = [];
var str = '';
//var obj = $("#"+ele);
var ele = $('#accordion');
$(obj+': checked').each(function(){
allVals.push($(this.val()));
dte.list.push($(this.val()));
str += '<li>'+$(this.val())+'</li>';
});
$('#verticle').text(str);
alert('List: ' + toString(list));
*/
}
};
dte.init();
init: function() {
$('.group input:checkbox').bind("click", dte.adjustList);
},
you only need to bind once based on your selector.
init: function() {
var chkbx = $('.group input[type="checkbox"]:checkbox');
$(chkbx).bind("click", dte.adjustList);
},
fiddle
I have edited your fiddle, I removed the for loop. Here is the link updated fiddle
You only need to bind the click event once.
You are binding events multiple time. You can do something like this:
init:function(){
$('.group input[type="checkbox"]:checkbox').bind('click',dte.adjustList);
},
Edited your fiddle:
http://jsfiddle.net/emphaticsunshine/tPAmc/
I have the the following code
<b class="xyzxterms" style="cursor: default; ">bryant keil bio</b>
How would I replace the b tag to a h1 tag but keep all other attributes and information?
Here's one way you could do it with jQuery:
var attrs = { };
$.each($("b")[0].attributes, function(idx, attr) {
attrs[attr.nodeName] = attr.nodeValue;
});
$("b").replaceWith(function () {
return $("<h1 />", attrs).append($(this).contents());
});
Example: http://jsfiddle.net/yapHk/
Update, here's a plugin:
(function($) {
$.fn.changeElementType = function(newType) {
var attrs = {};
$.each(this[0].attributes, function(idx, attr) {
attrs[attr.nodeName] = attr.nodeValue;
});
this.replaceWith(function() {
return $("<" + newType + "/>", attrs).append($(this).contents());
});
};
})(jQuery);
Example: http://jsfiddle.net/mmNNJ/
Not sure about jQuery. With plain JavaScript you could do:
var new_element = document.createElement('h1'),
old_attributes = element.attributes,
new_attributes = new_element.attributes;
// copy attributes
for(var i = 0, len = old_attributes.length; i < len; i++) {
new_attributes.setNamedItem(old_attributes.item(i).cloneNode());
}
// copy child nodes
do {
new_element.appendChild(element.firstChild);
}
while(element.firstChild);
// replace element
element.parentNode.replaceChild(new_element, element);
DEMO
Not sure how cross-browser compatible this is though.
A variation could be:
for(var i = 0, len = old_attributes.length; i < len; i++) {
new_element.setAttribute(old_attributes[i].name, old_attributes[i].value);
}
For more information see Node.attributes [MDN].
#jakov and #Andrew Whitaker
Here is a further improvement so it can handle multiple elements at once.
$.fn.changeElementType = function(newType) {
var newElements = [];
$(this).each(function() {
var attrs = {};
$.each(this.attributes, function(idx, attr) {
attrs[attr.nodeName] = attr.nodeValue;
});
var newElement = $("<" + newType + "/>", attrs).append($(this).contents());
$(this).replaceWith(newElement);
newElements.push(newElement);
});
return $(newElements);
};
#Jazzbo's answer returned a jQuery object containing an array of jQuery objects, which wasn't chainable. I've changed it so that it returns an object more similar to what $.each would have returned:
$.fn.changeElementType = function (newType) {
var newElements,
attrs,
newElement;
this.each(function () {
attrs = {};
$.each(this.attributes, function () {
attrs[this.nodeName] = this.nodeValue;
});
newElement = $("<" + newType + "/>", attrs).append($(this).contents());
$(this).replaceWith(newElement);
if (!newElements) {
newElements = newElement;
} else {
$.merge(newElements, newElement);
}
});
return $(newElements);
};
(Also did some code cleanup so it passes jslint.)
Only way I can think of is to copy everything over manually: example jsfiddle
HTML
<b class="xyzxterms" style="cursor: default; ">bryant keil bio</b>
Jquery/Javascript
$(document).ready(function() {
var me = $("b");
var newMe = $("<h1>");
for(var i=0; i<me[0].attributes.length; i++) {
var myAttr = me[0].attributes[i].nodeName;
var myAttrVal = me[0].attributes[i].nodeValue;
newMe.attr(myAttr, myAttrVal);
}
newMe.html(me.html());
me.replaceWith(newMe);
});
#Andrew Whitaker: I propose this change:
$.fn.changeElementType = function(newType) {
var attrs = {};
$.each(this[0].attributes, function(idx, attr) {
attrs[attr.nodeName] = attr.nodeValue;
});
var newelement = $("<" + newType + "/>", attrs).append($(this).contents());
this.replaceWith(newelement);
return newelement;
};
Then you can do things like: $('<div>blah</div>').changeElementType('pre').addClass('myclass');
I like the idea of #AndrewWhitaker and others, to use a jQuery plugin -- to add the changeElementType() method. But a plugin is like a blackbox, no mater about the code, if it is litle and works fine... So, performance is required, and is most important than code.
"Pure javascript" have better performance than jQuery: I think that #FelixKling's code have better performance than #AndrewWhitaker's and others.
Here a "pure Javavascript" (and "pure DOM") code, encapsulated into a jQuery plugin:
(function($) { // #FelixKling's code
$.fn.changeElementType = function(newType) {
for (var k=0;k<this.length; k++) {
var e = this[k];
var new_element = document.createElement(newType),
old_attributes = e.attributes,
new_attributes = new_element.attributes,
child = e.firstChild;
for(var i = 0, len = old_attributes.length; i < len; i++) {
new_attributes.setNamedItem(old_attributes.item(i).cloneNode());
}
do {
new_element.appendChild(e.firstChild);
}
while(e.firstChild);
e.parentNode.replaceChild(new_element, e);
}
return this; // for chain... $(this)? not working with multiple
}
})(jQuery);
Here is a method I use to replace html tags in jquery:
// Iterate over each element and replace the tag while maintaining attributes
$('b.xyzxterms').each(function() {
// Create a new element and assign it attributes from the current element
var NewElement = $("<h1 />");
$.each(this.attributes, function(i, attrib){
$(NewElement).attr(attrib.name, attrib.value);
});
// Replace the current element with the new one and carry over the contents
$(this).replaceWith(function () {
return $(NewElement).append($(this).contents());
});
});
With jQuery without iterating over attributes:
The replaceElem method below accepts old Tag, new Tag and context and executes the replacement successfully:
replaceElem('h2', 'h1', '#test');
function replaceElem(oldElem, newElem, ctx) {
oldElems = $(oldElem, ctx);
//
$.each(oldElems, function(idx, el) {
var outerHTML, newOuterHTML, regexOpeningTag, regexClosingTag, tagName;
// create RegExp dynamically for opening and closing tags
tagName = $(el).get(0).tagName;
regexOpeningTag = new RegExp('^<' + tagName, 'i');
regexClosingTag = new RegExp(tagName + '>$', 'i');
// fetch the outer elem with vanilla JS,
outerHTML = el.outerHTML;
// start replacing opening tag
newOuterHTML = outerHTML.replace(regexOpeningTag, '<' + newElem);
// continue replacing closing tag
newOuterHTML = newOuterHTML.replace(regexClosingTag, newElem + '>');
// replace the old elem with the new elem-string
$(el).replaceWith(newOuterHTML);
});
}
h1 {
color: white;
background-color: blue;
position: relative;
}
h1:before {
content: 'this is h1';
position: absolute;
top: 0;
left: 50%;
font-size: 5px;
background-color: black;
color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="test">
<h2>Foo</h2>
<h2>Bar</h2>
</div>
Good Luck...
Javascript solution
Copy the attributes of old element to the new element
const $oldElem = document.querySelector('.old')
const $newElem = document.createElement('div')
Array.from($oldElem.attributes).map(a => {
$newElem.setAttribute(a.name, a.value)
})
Replace the old element with the new element
$oldElem.parentNode.replaceChild($newElem, $oldElem)
Here is my version. It's basically #fiskhandlarn's version, but instead of constructing a new jQuery object, it simply overwrites the old elements with the newly created ones, so no merging is necessary.
Demo: http://jsfiddle.net/0qa7wL1b/
$.fn.changeElementType = function( newType ){
var $this = this;
this.each( function( index ){
var atts = {};
$.each( this.attributes, function(){
atts[ this.name ] = this.value;
});
var $old = $(this);
var $new = $('<'+ newType +'/>', atts ).append( $old.contents() );
$old.replaceWith( $new );
$this[ index ] = $new[0];
});
return this;
};