I'm creating a to do list with vanilla JS. I ideally want to keep my adding, deleting functions separate to keep everything tidy.
My issue is if I create a delete button when I add a to do I don't know how to reference the delete button.
JSFiddle: https://jsfiddle.net/ped1j6kf/11/
Any ideas?
Thanks
HTML
<body>
<!-- Main -->
<div id="main"></div>
<!-- Add -->
<input id="to-do-value" type="text" placeholder="Enter to do">
<button id="add">Add</button>
<script type="text/javascript" src="main.js"></script>
</body>
JS
// To do
var toDo = {
cacheDom: function() {
this.toDo = ['test'];
this.main = document.getElementById('main');
this.add = document.getElementById('add');
this.toDoValue = document.getElementById('to-do-value');
},
init: function() {
this.cacheDom();
this.bindEvents();
this.displayToDos();
},
bindEvents() {
this.add.addEventListener("click", this.addToDo.bind(this));
},
displayToDos: function() {
var html = '<ul>';
for(i=0; i < this.toDo.length; i++) {
html += '<li>' + this.toDo[i] + '</li>' + '<button>delete</button>';
}
html += '</ul>';
this.main.innerHTML = html;
},
addToDo(){
var toDoValue = this.toDoValue.value;
this.toDo.push(toDoValue);
this.displayToDos();
},
deleteToDo() {
console.log("make this delete button work");
}
}
toDo.init();
With some minor changes, you can make it work the way you have it now.
One of the changes would be that you could theoretically have multiple to do items named the same (for some reason), it might simply be easier to store the todo as an object, and save it in your todo list with an identifier, like so:
addToDo( text ){
this.toDo.push({ id: this._id++, text: text});
this.displayToDos();
}
This does require some other minor changes, but it offers the possibility to reference the onClick event directly, like this:
displayToDos: function() {
var html = '<ul>';
for(i=0; i < this.toDo.length; i++) {
html += '<li>' + this.toDo[i].text + '</li>' + '<button onClick="toDo.deleteToDo(' + this.toDo[i].id + ')">delete</button>';
}
html += '</ul>';
this.main.innerHTML = html;
}
You now have both a text that is displayed in the todo list as an Id that can be referenced when you want to delete that data
And then the delete function works like this
deleteToDo( id ) {
for (var i = 0; i < this.toDo.length; i++) {
if (this.toDo[i].id === id) {
// removes 1 item from the array at position i
this.toDo.splice(i, 1);
break;
}
}
this.displayToDos();
}
var toDo = {
_id: 0,
cacheDom: function() {
this.toDo = [];
this.main = document.getElementById('main');
this.add = document.getElementById('add');
this.toDoValue = document.getElementById('to-do-value');
},
init: function() {
// must run first
this.cacheDom();
this.bindEvents();
// now it can also allow for adding
this.addToDo('test');
this.displayToDos();
},
bindEvents() {
this.add.addEventListener("click", () => this.addToDo(this.toDoValue.value));
},
displayToDos: function() {
var html = '<ul>';
for(i=0; i < this.toDo.length; i++) {
html += '<li>' + this.toDo[i].text + '</li>' + '<button onClick="toDo.deleteToDo(' + this.toDo[i].id + ')">delete</button>';
}
html += '</ul>';
this.main.innerHTML = html;
},
addToDo( text ){
var toDoValue = text;
this.toDo.push({ id: this._id++, text: toDoValue});
this.displayToDos();
},
deleteToDo( id ) {
for (var i = 0; i < this.toDo.length; i++) {
if (this.toDo[i].id === id) {
this.toDo.splice(i, 1);
break;
}
}
this.displayToDos();
}
}
toDo.init();
<body>
<!-- Main -->
<div id="main"></div>
<!-- Add -->
<input id="to-do-value" type="text" placeholder="Enter to do">
<button id="add">Add</button>
<script type="text/javascript" src="main.js"></script>
</body>
I am assuming you want to reference the delete button so that if some one clicks it you want to perform a delete operation. Easiest way to do this would be :-
html += '<li>' + this.toDo[i] + '<button onclick="deleteSomething(this)">delete</button></li>';
Please note that the button element now comes under li.
and then in the deleteSomething function you get the parent element and delete it :--
function deleteSomething(el){
val = el.value;
i = this.toDo.indexOf(val);
if (i > -1) {
array.splice(i, 1);
}
el.parentElement.remove();
}
Related
I have created a custom print function using javascript (it closely models this tutorial https://levelup.gitconnected.com/pretty-print-your-site-with-javascript-d69f63956529). When I am only overriding the 'ctrl + p' shortcut it is running acceptably, but when I add a button to invoke the function it causes both the 'ctrl + p' and the button print to give the following error 'NotSupportedError: Window.print: Clone operation for printing failed'.
This is only happening in Firefox and the code works fine in Chrome.
Below is some of the code
Javascript code
const NsPrettyPrintPage = {
print: function () {
// create a hidden iframe named PrettyPrintFrame
const prettyPrintIframe = document.createElement('iframe');
prettyPrintIframe.setAttribute('id', 'PrettyPrintFrame');
prettyPrintIframe.setAttribute('name', 'PrettyPrintFrame');
prettyPrintIframe.setAttribute('style', 'display: none;');
// add newly created iframe to the current DOM
document.body.appendChild(prettyPrintIframe);
// add generated header content
prettyPrintIframe.contentWindow.document.head.innerHTML = this.generateHeaderHtml();
// add generated body content
prettyPrintIframe.contentWindow.document.body.innerHTML = this.generatePrintLayout();
document.getElementById("PrettyPrintFrame").contentWindow.print();
},
generatePrintLayout: function () {
// this function houses your default header/footer details and the switch to identify your pages by
let html = '';
html += '<h1 class="page-header"><img src="images/logo.png"></h1>'
html += this.calculatorResults();
// global footer elements
html += this.generateFooterHtml();
return html;
},
generateHeaderHtml: function () {
let headerHtml = '';
headerHtml += '<html><body><head>';
// loop through the styleSheets object and pull in all styles
for (let i = 0; i < document.styleSheets.length; i++) {
headerHtml += '<style>';
try {
for (let j = 0; j < document.styleSheets[i].cssRules.length; j++) {
headerHtml += document.styleSheets[i].cssRules[j].cssText || '';
}
} catch(e) {}
headerHtml += '</style>';
}
headerHtml += this.generateGlobalCss();
headerHtml += '</head>';
return headerHtml;
},
generateGlobalCss: function () {
// add any global css you want to apply to all pretty print pages
let css = '<style>';
// global css
css += 'body { padding: 40px 24px; }';
css += 'table tr { page-break-inside: avoid; }';
css += 'table td { vertical-align: top; padding: 4px 8px;}';
css += '#blank_row { display: none; }';
css += '#page { margin: 0.8cm; }';
css += 'table thead { display: none; }';
css += 'table td b { background-color: yellow !important; }';
css += '</style>';
return css;
},
generateFooterHtml: function () {
let footerHtml = '</body></html>';
return footerHtml;
},
calculatorResults: function() {
let html = '';
let resultItems = document.querySelectorAll('.table');
// iterate over result items
resultItems.forEach(function(item) {
html += item.outerHTML;
});
html += '<table class="table table-bordered"><tr><td></td><td>Indirect Fired</td><td>Direct Fired</td><td>Hydronic Fired</td><td>Electric</td></tr>';
html += '<tr>';
html += '<td>Total Project Cost</td><td><b>';
html += document.querySelector('#final_project_cost_indirect_air').innerHTML;
html += '</b></td>';
html += '<td><b>';
html += document.querySelector('#final_project_cost_open_flame').innerHTML;
html += '</b></td>';
html += '<td><b>';
html += document.querySelector('#final_project_cost_hydronic').innerHTML;
html += '</b></td>';
html += '<td><b>';
html += document.querySelector('#final_project_cost_electric_output').innerHTML;
html += '</b></td></tr></table>';
return html;
}
};
// override Ctrl/Cmd + P
document.addEventListener("keydown", function (event) {
if((event.ctrlKey || event.metaKey) && event.key == "p") {
NsPrettyPrintPage.print();
event.preventDefault();
return false;
}
} , false);
Button I added that seems to break the printing when using firefox.
<button type="button" class="btn btn-primary" onclick="NsPrettyPrintPage.print();"><i class="fa fa-file-pdf-o"></i>Print</button>
Update:
It appears to be working if I removed all the Javascript libraries that were included. There is an error in the Javascript when the page loads when it starts Bootstrap. The line below that says 'FormPlugins.init();'.
<script>
$(document).ready(function() {
App.init();
DashboardV2.init();
FormSliderSwitcher.init();
ChartNvd3.init();
FormPlugins.init();
});
$('#heatcalc_form').on('keyup keypress', function(e)
{
var keyCode = e.keyCode || e.which;
if (keyCode === 13)
{
e.preventDefault();
return false;
}
});
</script>
That error is 'TypeError: $(...).colorpicker is not a function'.
The function is working now with this code as the print function.
print: function () {
// create a hidden iframe named PrettyPrintFrame
const prettyPrintIframe = document.createElement('iframe');
prettyPrintIframe.setAttribute('id', 'PrettyPrintFrame');
prettyPrintIframe.setAttribute('name', 'PrettyPrintFrame');
prettyPrintIframe.setAttribute('style', 'display: none;');
// add newly created iframe to the current DOM
document.body.appendChild(prettyPrintIframe);
setTimeout(function ()
{
// add generated header content
prettyPrintIframe.contentWindow.document.head.innerHTML = NsPrettyPrintPage.generateHeaderHtml();
// add generated body content
prettyPrintIframe.contentWindow.document.body.innerHTML = NsPrettyPrintPage.generatePrintLayout();
}, 100);
setTimeout(function ()
{
document.getElementById("PrettyPrintFrame").contentWindow.print();
}, 100);
}
It turned out that it was a 'racing' error. This, Firefox won't print unless I set a breakpoint?, answer pointed me in the right direction.
So i've been messing around and searching and it triggers when i don't generated the code via my JS and put the output as html on the file.
The menu is supposed to be dynamic and generate subs on it and subs have products and so on i was doing it and logically is correct but can't know if JS is confliting some part of this code.
JS Generate Menus:
$(function(){
var ul = $('#category-content');
var subcategoryLi = $('#sublist');
var productsLi = $('#productList');
init();
function init(){
GetCategorys();
}
function BuildSubCategorys(id,sub){
var content = '';
if (typeof(sub) != 'undefined' && sub.length > 0){
for(var i = 0; i < sub.length; i++){
if (sub[i].c_parentcat == id){
content += AddSubItem(sub[i].c_name,sub[i].c_id);
}
}
return content;
}
else{
return '';
}
}
function hasSubs(j,d){
for(var i=0;i<d.length;i++){
if(d[i].c_parentcat == j ){
return true;
}
}
}
function BuildCategorys(root){
var content = AddCategoryHeader('Categorias');
var subs = [];
if (typeof(root) != 'undefined' && root.length > 0){
for(var i = 0; i < root.length; i++){
if (root[i].c_parentcat == -1){
content += AddCategory(root[i].c_name,root[i].c_id);
if (hasSubs(root[i].c_id, root) == true){
var subContent = BuildSubCategorys(root[i].c_id, root);
subs[root[i].c_id] = CreateSubList(subContent);
}
}
}
ul.append(content);
ul.append(AddSeparator());
for(var j = 0; j < root.length; j++){
curr_id = root[j].c_id;
ul.find('#category'+curr_id).append(subs[curr_id]);
}
}
else {
ul.append(AddCategoryHeader('Categorias'));
ul.append(HandleNoSubData());
ul.append(AddSeparator());
}
//Build the products
GetCategoryProducts();
}
function BuildProducts(p){
var content = AddCategoryHeader('Produtos sem categoria');
var category_items = [];
if (typeof(p) != 'undefined' && p.length > 0){
for(var i=0; i < p.length; i++){
if (p[i].p_categoryid == -1){
//he has no category so lets add it to the non category section
content += AddProduct(p[i].p_name,p[i].p_id);
}
else {
subcategoryLi.find('#subcategory'+ p[i].p_categoryid).append(AddProduct(p[i].p_name,p[i].p_id));
}
}
ul.append(content);
//LEFT: LINK ON THE PRODUCT WITH THEIR ID ON CREATE FUNCTION
}else{
ul.append(AddCategoryHeader('Produtos sem categoria'))
ul.append(HandleNoProdData());
}
}
function AddSeparator(){
return '<li role="separator" class="divider"></li>';
}
function AddCategoryHeader(name){
return '<li class="dropdown-header">' + name +'</li>';
}
function AddCategory(name,id){
return '<li class="dropdown-submenu" id="category'+id+'"><a id="menu-itex" tabindex="-1" href="javascript:;">' + name + ' <span class="caret"></span></a></li>';
}
function AddProduct(name,id){
return '<li>' + name + '</li>';
}
function AddSubItem(name,id){
return '<li> '+ name + ' </li>';
}
function CreateSubList(c){
return '<ul id="sublist" class="dropdown-menu">'+ c +'</ul>';
}
function CreateProductsList(){
return '<li class="dropdown"><ul id="productList" class="dropdown-menu">'+ c +'</ul></li>';
}
function HandleNoData(){
return '<li> Não existem categorias </li>';
}
function HandleNoSubData(){
return '<li> Não existem sub-categorias </li>';
}
function HandleNoProdData(){
return '<li> Não existem produtos </li>';
}
function GetCategorys(){
var url = base_url + 'home/ajaxCategorys';
$.post(url,function(js){
if (js != null && js != 'false')
BuildCategorys(JSON.parse(js));
else
return false;
});
}
function GetCategoryProducts(){
var url = base_url + 'home/ajaxCategoryProducts';
$.post(url,function(js){
if (js != null && js != 'false')
BuildProducts(JSON.parse(js));
else
return false;
});
}
$(document).ready(function(){
$('#menu-itex').on("click" ,function(e){
console.log('Click for menu');
$(this).next('ul').toggle();
e.stopPropagation();
e.preventDefault();
});
});
});
The output of my code directly as html it works but if i do in this way with JS script on generating it it doesn't trigger on menu-itex id click and i also tried by using the class. Got the trigger idea from w3wschools boostrap advanced menus.
When you add a event listener to a generated element before the item is generated (happens sometimes), it'll not trigger the event on click.
The best solution is to append the click event to body (or the item container that is rendered on page load) and listen to click only if it's inside the desired element.
It goes something like this:
//$('body').on('click', <-- would work too, but it's not delimited to a certain section of your page.
$('#category-content').on('click', '#menu-itex', function(){
//Your stuff here
})
I have one problem with click function. I have created this demo from jsfiddle.net.
In this demo you can see there are smile buttons. When you click those buttons then a tab will be opening on that time. If you click the red button from tab area then the tab is not working there are something went wrong.
Anyone can help me here what is the problem and what is the solution?
The tab is normalize like this working demo
var response = '<div class="icon_b">
<div class="clickficon"></div>
<div class="emicon-menu MaterialTabs">
<ul>
<li class="tab active"> TAB1</li>
<li class="tab"> TAB2</li>
<li class="tab"> TAB3<span></span></li>
</ul>
<div class="panels">
<div id="starks-panel1" class="panel pactive"> a </div>
<div id="lannisters-panel1" class="panel"> b </div>
<div id="targaryens-panel1" class="panel"> c </div>
</div>
</div>
</div>';
$(document).ready(function () {
function showProfileTooltip(e, id) {
//send id & get info from get_profile.php
$.ajax({
url: '/echo/html/',
data: {
html: response,
delay: 0
},
method: 'post',
success: function (returnHtml) {
e.find('.user-container').html(returnHtml).promise().done(function () {
$('.emoticon').addClass('eactive');
});
}
});
}
$('body').on('click', '.emoticon', function(e) {
var id = $(this).find('.emoticon_click').attr('data-id');
showProfileTooltip($(this), id);
});
$(this).on( "click", function() {
$(this).find('.user-container').html("");
});
var componentHandler = function() {
'use strict';
var registeredComponents_ = [];
var createdComponents_ = [];
function findRegisteredClass_(name, opt_replace) {
for (var i = 0; i < registeredComponents_.length; i++) {
if (registeredComponents_[i].className === name) {
if (opt_replace !== undefined) {
registeredComponents_[i] = opt_replace;
}
return registeredComponents_[i];
}
}
return false;
}
function upgradeDomInternal(jsClass, cssClass) {
if (cssClass === undefined) {
var registeredClass = findRegisteredClass_(jsClass);
if (registeredClass) {
cssClass = registeredClass.cssClass;
}
}
var elements = document.querySelectorAll('.' + cssClass);
for (var n = 0; n < elements.length; n++) {
upgradeElementInternal(elements[n], jsClass);
}
}
function upgradeElementInternal(element, jsClass) {
if (element.getAttribute('data-upgraded') === null) {
element.setAttribute('data-upgraded', '');
var registeredClass = findRegisteredClass_(jsClass);
if (registeredClass) {
createdComponents_.push(new registeredClass.classConstructor(element));
} else {
createdComponents_.push(new window[jsClass](element));
}
}
}
function registerInternal(config) {
var newConfig = {
'classConstructor': config.constructor,
'className': config.classAsString,
'cssClass': config.cssClass
};
var found = findRegisteredClass_(config.classAsString, newConfig);
if (!found) {
registeredComponents_.push(newConfig);
}
upgradeDomInternal(config.classAsString);
}
return {
upgradeDom: upgradeDomInternal,
upgradeElement: upgradeElementInternal,
register: registerInternal
};
}();
function MaterialTabs(element) {
'use strict';
this.element_ = element;
this.init();
}
MaterialTabs.prototype.Constant_ = {
MEANING_OF_LIFE: '42',
SPECIAL_WORD: 'HTML5',
ACTIVE_CLASS: 'pactive'
};
MaterialTabs.prototype.CssClasses_ = {
SHOW: 'materialShow',
HIDE: 'materialHidden'
};
MaterialTabs.prototype.initTabs_ = function(e) {
'use strict';
this.tabs_ = this.element_.querySelectorAll('.tab');
this.panels_ = this.element_.querySelectorAll('.panel');
for (var i=0; i < this.tabs_.length; i++) {
new MaterialTab(this.tabs_[i], this);
}
};
MaterialTabs.prototype.resetTabState_ = function() {
for (var k=0; k < this.tabs_.length; k++) {
this.tabs_[k].classList.remove('pactive');
}
};
MaterialTabs.prototype.resetPanelState_ = function() {
for (var j=0; j < this.panels_.length; j++) {
this.panels_[j].classList.remove('pactive');
}
};
function MaterialTab (tab, ctx) {
if (tab) {
var link = tab.querySelector('a');
link.addEventListener('click', function(e){
e.preventDefault();
var href = link.href.split('#')[1];
var panel = document.querySelector('#' + href);
ctx.resetTabState_();
ctx.resetPanelState_();
tab.classList.add('pactive');
panel.classList.add('pactive');
});
}
};
MaterialTabs.prototype.init = function() {
if (this.element_) {
this.initTabs_();
}
}
window.addEventListener('load', function() {
componentHandler.register({
constructor: MaterialTabs,
classAsString: 'MaterialTabs',
cssClass: 'MaterialTabs'
});
});
});
There is updated and working version.
What we have to do, is to move the target on the same level as the icon is (almost like tab and content). Instead of this:
<div class="emoticon">
<div class="emoticon_click" data-id="1">
<img src="http://megaicons.net/static/img/icons_sizes/8/178/512/emoticons-wink-icon.png" width="30px" height="30px">
<div class="user-container" data-upgraded></div>
</div>
</div>
We need this
<div class="emoticon">
<div class="emoticon_click" data-id="1">
<img src="http://megaicons.net/static/img/icons_sizes/8/178/512/emoticons-wink-icon.png" width="30px" height="30px">
// not a child
<!--<div class="user-container" data-upgraded></div>-->
</div>
// but sibling
<div class="user-container" data-upgraded></div>
</div>
And if this is new HTML configuration, we can change the handlers
to target click on div "emoticon_click"
change the content of the sibling (not child) div "user-container"
The old code to be replaced
$('body').on('click', '.emoticon', function(e) {
var id = $(this).find('.emoticon_click').attr('data-id');
showProfileTooltip($(this), id);
});
$(this).on( "click", function() {
$(this).find('.user-container').html("");
});
will now be replaced with this:
//$('body').on('click', '.emoticon', function(e) {
$('body').on('click', '.emoticon_click', function(e) {
// clear all user container at the begining of this click event
$('body').find('.user-container').html("");
// find id
var id = $(this).attr('data-id');
// find the parent, which also contains sibling
// user-container
var parent = $(this).parent()
// let the target be initiated
showProfileTooltip($(parent), id);
});
$(this).on( "click", function() {
//$(this).find('.user-container').html("");
});
Check it in action here
NOTE: the really interesting note was in this Answer by pinturic
If we want to extend the first and complete answer with a feature:
close all tabs if clicked outside of the area of tabs or icons
we just have to
add some event e.g. to body
and do check if the click was not on ".emoticon" class elements
There is a working example, containing this hook:
$('body').on( "click", function(e) {
// if clicked in the EMOTICON world...
var isParentEmotion = $(e.toElement).parents(".emoticon").length > 0 ;
if(isParentEmotion){
return; // get out
}
// else hide them
$('body').find('.user-container').html("");
});
I have been debugging your code and this is the result:
you are adding the "tab" under ; any time you click within that div this code is intercepting it:
$('body').on('click', '.emoticon', function(e) {
var id = $(this).find('.emoticon_click').attr('data-id');
showProfileTooltip($(this), id);
});
and thus the "tab" are built again from scratch.
This is a plugin that filter an li elements.
jquery.livefilter.js:
/*
* jQuery.liveFilter
*
* Copyright (c) 2009 Mike Merritt
*
* Forked by Lim Chee Aun (cheeaun.com)
*
*/
(function($){
$.fn.liveFilter = function(inputEl, filterEl, options){
var defaults = {
filterChildSelector: null,
filter: function(el, val){
return $(el).text().toUpperCase().indexOf(val.toUpperCase()) >= 0;
},
before: function(){},
after: function(){}
};
var options = $.extend(defaults, options);
var el = $(this).find(filterEl);
if (options.filterChildSelector) el = el.find(options.filterChildSelector);
var filter = options.filter;
$(inputEl).keyup(function(){
var val = $(this).val();
var contains = el.filter(function(){
return filter(this, val);
});
var containsNot = el.not(contains);
if (options.filterChildSelector){
contains = contains.parents(filterEl);
containsNot = containsNot.parents(filterEl).hide();
}
options.before.call(this, contains, containsNot);
contains.show();
containsNot.hide();
if (val === '') {
contains.show();
containsNot.show();
}
options.after.call(this, contains, containsNot);
});
}
})(jQuery);
HTML:
<!DOCTYPE html>
<meta charset="utf-8">
<script src="jquery.min.js"></script>
<script src="jquery.livefilter.js"></script>
<script>
$(function(){
$('#livefilter-list').liveFilter('#livefilter-input', 'li', {
filterChildSelector: 'a'
});
});
</script>
<div>
<fieldset>
<legend><label for="livefilter-input">Search...</label></legend>
<input id="livefilter-input" type="text" value="">
<ul id="livefilter-list">
<li>cool</li>
<li>nice</li>
<li>interesting</li>
<li>javascript</li>
<li>css</li>
<li>html</li>
<li>script</li>
<li>international</li>
</ul>
</fieldset>
</div>
This code works great, but only with static li elements, like the li tags up there.
I have a jQuery function that append to ul tag many li elements. But, in this case, doesn't work
My code:
<div id="users" class="nav nav-second-level">
<ul id="livefilter-list" aria-expanded="true">
<li><a>Ciao</a></li>
<li><a>Come</a></li>
<li><a>Stai</a></li>
</ul>
</div>
jQuery:
for (var i in arr) {
$('#users').append('<li data-user="' + arr[i].id + '">' + arr[i].name + '</li>');
}
How can I resolve?
UPDATE:
<ul id="livefilter-list" class="nav nav-second-level" aria-expanded="true">
<li><a>Hey</a></li>
<li><a>How are</a></li>
<li><a>you?</a></li>
</ul>
I append others li element with:
for (var i in arr) {
$('#livefilter-list').append('<li data-user="' + arr[i].id + '">' + arr[i].name + '</li>');
}
and then:
$(function () {
$('#livefilter-list').liveFilter('#livefilter-input', 'li', {
filterChildSelector: 'a'
});
});
With "Hey", "How are" and "you?" works. But with the li elements added with jQuery not. Why?
This syntax :
$(function (){...});
makes the anonymous function passed in parameter executed as soon as the dom is loaded. This means :
function () {
$('#livefilter-list').liveFilter('#livefilter-input', 'li', {
filterChildSelector: 'a'
}
is run before the for loop apending elements to the livefilter-list :
for (var i in arr) {
$('#livefilter-list').append('<li data-user="' + arr[i].id + '">' + arr[i].name + '</li>');
}
I suggest you try to call liveFilter once the elements are appended to the livefilter-list :
for (var i in arr) {
$('#livefilter-list').append('<li data-user="' + arr[i].id + '">' + arr[i].name + '</li>');
}
$('#livefilter-list').liveFilter('#livefilter-input', 'li', {
filterChildSelector: 'a'
});
As your ul is #livefilter-list, you should use:
for (var i in arr) {
$('#livefilter-list').append('<li data-user="' + arr[i].id + '">' + arr[i].name + '</li>');
}
So it'll append li to <ul id="livefilter-list" aria-expanded="true">, not <div id="users" class="nav nav-second-level">
As your liveFilter is register to li under #livefilter-input, you can't have effect on #user > li, which become sibling of #livefilter-input.
Edit:
By the source code of jQuery.liveFilt,
var el = $(this).find(filterEl);
if (options.filterChildSelector) el = el.find(options.filterChildSelector);
it caches the element when created, and will not monitor further add/remove of the monitor target anymore, so it won't filter out any li added after its initialization.
You can tweak its code to force it get the newest element when filter is about to happen:
var el = $(this).find(filterEl);
if (options.filterChildSelector) el = el.find(options.filterChildSelector);
var self = this;
var filter = options.filter;
$(inputEl).keyup(function(){
el = $(self).find(filterEl);
if (options.filterChildSelector) el = el.find(options.filterChildSelector);
var val = $(this).val();
var contains = el.filter(function(){
return filter(this, val);
});
var containsNot = el.not(contains);
if (options.filterChildSelector){
contains = contains.parents(filterEl);
containsNot = containsNot.parents(filterEl).hide();
}
options.before.call(this, contains, containsNot);
contains.show();
containsNot.hide();
if (val === '') {
contains.show();
containsNot.show();
}
options.after.call(this, contains, containsNot);
So now it works. See Demo
You are finding the filter elements once, and only once:
var el = $(this).find(filterEl);
if (options.filterChildSelector) el = el.find(options.filterChildSelector);
Later, on keyup you use this variable el to further filter the elements, however if you have added any new li between intialising the plugin, and pressing a key, the new elements are not included in the pre-defined list el.
The answer is to look for elements live on each keyup and not cache it away in a variable.
That should be as easy as wrapping the above 2 lines in a function:
function getFilterElements(){
var el = $(this).find(filterEl);
if (options.filterChildSelector) el = el.find(options.filterChildSelector);
return el;
}
And calling that later in keyup:
var self = this;
$(inputEl).keyup(function(){
var val = $(this).val();
var el = getFilterElements.call(self)
var contains = el.filter(function(){
return filter(this, val);
});
...
Live example: http://jsfiddle.net/56xsvosq/
SO I'm creating multiple info pictures with their own ids.
var inc = "";
for(int i = 0; i < itemList.length; i++)
{
var inf = "info";
var conc = inf.concat(i); //dinamically creating info0, info1, info2, info3.....
inc += "<span id=" + '"' + conc + '"' + ">";
inc += "<a class='infobutton' ";
inc += ' href="Javascript: void(0)">';
inc += '<img border="0" width="30" height="30" src="images/info.png"></a></span>';
}
So, each image is going to have an id called, info0, info1, etc..
And I want to use jquery to create individual dialogs to each image, and i'm using this.
$(function ()
{
for(var i = 0; i < itemList.length; i++)
{
$("#info" + i).on("click", function ()
{
$("#dialog").dialog("open");
});
}
$("#dialog").dialog(
{
autoOpen: false
});
$("#dialog").dialog(
{
modal: true,
resizable: false,
buttons:
{
"Close": function ()
{
$(this).dialog("close");
}
}
});
});
Needless to say, it's not working, and i don't have a clue, maybe i'm trying to use something and it's not how jquery works. Thanks.
Use http://api.jquery.com/delegate/ for the event. You inserting to the DOM dynamicly so they wont work on inserted elements.
$('body').delegate($("#info"+i), 'click', function(){
$("#dialog").dialog("open");
});
Also it's better to use a class like .open_dialog and create the event once rather then attaching in the loop in every iteration to a single element.
I believe instead:
for(var i = 0; i>itemList.length; i++)
{
$("#info" + i).on("click", function ()
{
$("#dialog").dialog("open");
});
}
can do this(entering your images into a div):
$("div").on("click", "img", function{});