Hello I have created a list and sliced it up and added pagingnation.
All works good but I would like the user to be able to disable pagingnation and just use regular scroll.
My html mark-up looks somewhat like this atm.
<div data-bind="event: { mousewheel: ScrolledPagingnation }">
</div>
Im thinking a custom handler is the right tool for the job? but havent really have any succes adding/removing the event..
<div data-bind="toggleScroll: EnablePagingnation">
</div>
EnablePagingnation is just an observable toggeling a boolen.
How can I implement adding/removing of the 'event object on my element?
Any other suitable approach is welcome aswell.. :)
ko.bindingHandlers.toggleScroll = {
update: function (element, valueAccessor, allBindings) {
var value = valueAccessor();
if (ko.unwrap(value) === true) {
//Pseudo Code
element add "event: { mousewheel: ScrolledPagination }"
} else {
//Clean the Binding
ko.cleanNode(element);
}
}
};
Don't use cleanNode. Ever.
You don't even need a bindingHandler. Just use an expression for the event handler. I used the click event in the snippet below.
vm = {
active: ko.observable(false),
v: ko.observable(1),
addToV: function() {
vm.v(vm.v() + 1);
}
};
ko.applyBindings(vm);
#area {
height: 10rem;
width: 10rem;
background-color: lightgreen;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div id="area" data-bind="event: {click: active() ? addToV : null}"></div>
Activate:
<input type="checkbox" data-bind="checked: active" />
<div data-bind="text: v"></div>
Related
I am trying to use JQuery to update a data attribute after clicking on a button.
Please take a look at what I have tried below.
When I click on the div with class previous-wrapper, the whole block is well updated, and the data-nb-attribute get the correct value.
But, when I click on this div for the second time, data-nb-attribute gets the value NaN..
Is there another way to dynamically update and retrieve the value of this attribute?
$(function(){
$(document).on('click','.previous-wrapper',function(){
var get_nb = $(this).find('.previous');
get_nb = get_nb.data("nb");
get_nb = parseInt(get_nb)
//Dom to update
arr_left = $('<i/>', {
className: 'fa-solid fa-arrow-left'
});
previous = $('<div/>', {
className: 'previous',
'data-nb': get_nb-5,
html: "Previous"
});
//Question 2. How to append previous to arrow_left
$(".previous-wrapper").html(previous);
});
});
.previous-wrapper{
cursor: pointer;
background:red;
width:100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="previous-wrapper">
<i class="fa-solid fa-arrow-left"></i>
<span class="previous" data-nb="100">Previous</span>
</div>
I would also like to know how to add multiple DOM created by JQuery.
You're overwriting the HTML with an invalid attribute of "classname" instead of "class", which means on the second interation $('.previous') won't match anything.
Corrected version of your code:
$(document).on('click', '.previous-wrapper', function() {
var get_nb = $(this).find('.previous');
get_nb = get_nb.data("nb");
get_nb = parseInt(get_nb)
//Dom to update
arr_left = $('<i/>', {
class: 'fa-solid fa-arrow-left'
});
previous = $('<div/>', {
class: 'previous',
'data-nb': get_nb - 5,
html: "Previous"
});
// including both elements at once:
$(".previous-wrapper").html([arr_left, previous]);
console.log($('.previous-wrapper').html())
});
.previous-wrapper {
cursor: pointer;
background: red;
width: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="previous-wrapper">
<i class="fa-solid fa-arrow-left"></i>
<span class="previous" data-nb="100">Previous</span>
</div>
It would be much, much simpler, however, to simply update the one attribute you want to update, instead of rewriting all the HTML every click:
$(document).on('click', '.previous-wrapper', function() {
let el = $(this).find('.previous')
// update the data attribute using .attr() instead of .data() because jQuery handles .data internally; it's not reflected in the DOM
el.attr('data-nb', Number(el.attr('data-nb')) - 5);
console.log($('.previous-wrapper').html())
});
.previous-wrapper {
cursor: pointer;
background: red;
width: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="previous-wrapper">
<i class="fa-solid fa-arrow-left"></i>
<span class="previous" data-nb="100">Previous</span>
</div>
I want to add a dynamic classname which will come from javascript based on some check. But one class navigate-forward is there which is also displayed based on some check. How to combine both?
Following is my code -
<span class="menu-item" data-bind="text: data.description,
css: {
'navigate-forward': !child.action,
child.className //I tried something like this which doesn't work
}"></span>
child.className will have a value based on some check in javascript code. And whatever value it gives I want to add that class to this span.
Ex. if child.className is tooltip then that class should be added to the span.
output
<span class="menu-item navigate-forward tooltip">Menu item</span>
The css: binding only accepts a list of classes and whether to apply them or not. You could use:
<div data-bind="attr: { 'class': MyPropertyName }" class="initialClass"></div>
But this would overwrite any pre-existing classes (initialClass) on the element.
Take a look at https://github.com/knockout/knockout/wiki/Bindings---class where there is a custom binding handler that allows you to specify a dynamic class:
<div class="initialClass" data-bind="class: MyPropertyName"></div>
This solution will combine the existing class attribute with the MyPropertyName property.
function vm(){
var self = this;
self.className = ko.observable("class2");
}
(function(){
var viewModel = new vm();
ko.applyBindings(viewModel);
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<label class="class1" data-bind="css:className">Your Element</label>
You can also use the style and css bindings with an object that holds all your styles and classes instead of using individual class names. This allows for a more programmatic approach.
function viewModel(){
var self = this;
this.description = ko.observable('placeholder text');
this.useBlue = ko.observable(false);
this.borderClass = ko.observable("red-border");
this.classes = ko.pureComputed(function(){
var obj = {
'blue-background': self.useBlue()
};
obj[self.borderClass()] = true;
return obj;
});
}
ko.applyBindings(new viewModel());
.blue-background {
background-color: lightblue;
}
.red-border {
border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<span class="menu-item" data-bind="text: description, css: classes"></span>
<br/>
<input type="button" data-bind="click: function(){ useBlue(!useBlue()) }" value="toggle" />
I am new to knockout and am trying to check two booleans. Currently, I have:
data-bind="css: { hidden: bool1() }"
Hidden is a class I created in CSS. I need to check against two bools to use the class or not. It works fine with one bool. How do I use two?
I tried the following:
data-bind="css: { hidden: bool1() && bool2() }"
data-bind="css: { hidden: bool1() + bool2() }"
I'm just not getting it. Can anyone help please?
Here is a working example. The text is hidden if both boxes are checked. You need to call the observables when using them in an expression in the binding.
ko.applyBindings({
bool1: ko.observable(),
bool2: ko.observable()
});
.hidden {
visibility:hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="css:{hidden:bool1()&&bool2()}">I'm here!</div>
<input type="checkbox" data-bind="checked:bool1" />
<input type="checkbox" data-bind="checked:bool2" />
Update:
Oh! Silly me. Be sure to put in the parentheses after the bool1 and bool2.. so:
data-bind="css: {hidden: bool1(), hidden: bool2()}"
Also, be sure to put it in like a div tag, or whatever floats your boat.
There's a lot of documentation on http://knockoutjs.com/, so you should check that out of you haven't already. Tell me if it works.
This is another option (an extension to Roy J's), it keeps your html a little simpler, and makes refactoring easier if you use the combination of bools in more than one place.
bool1 = ko.observable();
bool2 = ko.observable();
ko.applyBindings({
bool1: bool1,
bool2: bool2,
shouldHideDiv: ko.computed(function () {
return bool1() && bool2();
})
});
.hidden {
visibility:hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="css:{hidden: shouldHideDiv}">I'm here!</div>
<input type="checkbox" data-bind="checked:bool1" />
<input type="checkbox" data-bind="checked:bool2" />
Within a Angular-Kendo treeview <div>, I'm having a bit of trouble styling a <div> element which I want to show on a hover event.
Here is an image of the treeview without the icon options to the right of each node:
However I'd like to show some icons to the right once I hover on a node, as follows :
your advice is appreciated...
Here's the HTML (please notice the Kendo k-template directive used here):
<div id="treeview" kendo-tree-view
k-options="nav.treeOptions"
k-data-source="nav.reportsTreeDataSource"
k-on-change="nav.onTreeSelect(dataItem)">
<span k-template>{{dataItem.text}}
<span class="templ-icons">
<a title="add new folder" ng-click="nav.addAfter(nav.selectedItem)"><i class="fa fa-folder-open"></i></a>
<a title="add report here" ng-click="nav.addBelow(nav.selectedItem)"><i class="fa fa-plus"></i></a>
<a title="remove" ng-click="nav.remove(nav.selectedItem)"><i class="fa fa-remove"></i></a>
</span>
</span>
</div>
and of course, I want ONLY want to show the icon options when a user hovers over any particular node (i.e. could be at the folder level, or at the leaf level).
and the CSS :
<style scoped>
.templ-icons {
text-align: center;
font-size:smaller;
font-style: italic;
color: white;
}
#treeview:hover > .templ-icons {
opacity:1.0;
display:none;
}
What if you make a small directive for this? Check out this example I put together with my toggle-preview directive. I'm not including any kendo controls, but you should be able to add something like this to your custom template. You might need to make some small changes, and you can of course make it more robust, but this seems like a good situation to use something like this, and you can use whichever element you would like be it <span> <div> etc.
JSFiddle Link
<span toggle-preview>item a</span>
.active::after {
content: url(~/icon.gif);
}
app.directive('togglePreview', [function () {
return {
restrict: 'A',
link: function (scope, elem, attrs) {
elem.on('mouseenter', function() {
elem.addClass('active');
});
elem.on('mouseleave', function() {
elem.removeClass('active');
});
}
}
}]);
Edit
Per discussion we want to attach all three pre-defined icons, and attach a ng-click handler to the icon with some sort of sense of knowing which icon we clicked. Here is a solution that is driven on a naming convention and utilizing the $compile service for directive injection based on the icon values we provide in the parent attr
Updated JSFiddle
<div toggle-preview ico="plus delete folder" >item a</div>
.add::after {
content: url(~/iconA.gif);
}
.delete::after {
content: url(~/iconB.gif);
}
.folder::after {
content: url(~/iconC.gif);
}
app.directive('togglePreview', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function (scope, elem, attrs) {
var classes = attrs.ico.split(' ');
elem.on('mouseenter', function(){
angular.forEach(classes, function(e, i) {
var node = $compile('<span ng-click="clickIco($event)"><img src="//~' + e + '.gif" ico="'+ e +'"/><span>')(scope);
elem.append(node);
})
});
elem.on('mouseleave', function(){
elem.children().remove();
});
scope.clickIco = function($event){
console.log($event.target.attributes.ico.value);
}
}
}
}]);
i have the following code that i was hoping would allow me to click a button and add another box to the page but inj stread it just resets the page every time i click one of the buttons
<style>
div.box
{
width: 50px;
height: 50px;
background-image: url("Images/smallBox.jpg");
}
div.largeBox
{
width: 100px;
height: 100px;
background-image: url("Images/largeBox.jpg");
}
</style>
<script type="text/javascript">
$(function () {
setupDragging();
});
function setupDragging() {
$(".box").draggable({ snap: true });
$(".largeBox").draggable({ snap: true });
}
function addSmallBox() {
var boxArea = document.getElementById('boxArea');
var newBox = document.createElement('div');
newBox.setAttribute('class', 'box');
boxArea.appendChild(newBox);
setupDragging();
}
function addLargeBox() {
var boxArea = document.getElementById('boxArea');
var newBox = document.createElement('div');
newBox.setAttribute('class', 'largeBox');
boxArea.appendChild(newBox);
setupDragging();
}
</script>
<form>
<button onclick="addSmallBox();">small box</button>
<button onclick="addLargeBox();">large box</button>
</form>
<div id="boxArea">
</div>
please can somebody let me know what i am doing wrong and how i can achieve what i want.
ANSWER:
just an update on the final result
<style>
div.box
{
width: 50px;
height: 50px;
background-image: url("../Images/smallBox.jpg");
position: absolute;
}
div.largeBox
{
width: 100px;
height: 100px;
background-image: url("../Images/largeBox.jpg");
position: absolute;
}
</style>
<script type="text/javascript">
$(function () {
setupDragging();
$('.addBox').click(function(e){
if($(this).hasClass('smallBox')) addSmallBox();
else if($(this).hasClass('largeBox')) addLargeBox();
e.preventDefault();
})
});
function setupDragging() {
$(".box").draggable({ snap: true });
$(".largeBox").draggable({ snap: true });
}
function addSmallBox() {
$('<div>', { class: 'box' }).appendTo('#boxArea')
setupDragging();
}
function addLargeBox() {
$('<div>', { class: 'largeBox' }).appendTo('#boxArea')
setupDragging();
}
</script>
<form>
<button class='addBox smallBox'>small box</button>
<button class='addBox largeBox'>large box</button>
</form>
<div id="boxArea">
</div>
i made use of several of the answers below and this was the final result but went down the html5 route in the end.
First of all, since you are using jQuery, if I were you, I would not use the onclick attribute in your button. Instead, add an event listener like so:
$('button').click(function(){
addLargeBox();
return false;
});
OR
$('button').click(function(e){
addLargeBox();
e.preventDefault();
});
Both of which will prevent the user's browser from following the link but will execute the JavaScript as you want.
Also, since you require two different functions to be executed depending on which button is clicked, you should probably add a class or id to differentiate the two.
Your markup would then look like this:
<button class="add-box">small box</button>
<button class="add-box large-box">large box</button>
And your JavaScript would be:
$('.add-box').click(function(){
if ($(this).hasClass('large-box')) addLargeBox();
else addSmallBox();
return false;
});
The button tag you are using is a HTML5 version of a form submit button which causes a page refresh.
Change the buttons to:
<input type="button" onclick="addSmallBox();" value="small box" />
Or put return false at the bottom of each of your javascript functions.
Couple of things:
Always separate JS from code (that means lose the onclick attributes and bind them in javascript instead.)
The form is still being submitted; have your functions return false/preventDefault (jQuery) to avoid this.
Some hints:
Since you're already using jQuery, why not build html elements with it? ($('<div>',{class:'box'}).appendTo('#boxArea') for instance)
You have to return false from the click event to prevent the default browser behavior of following the link.
I'm not sure why just by glancing at the code, but perhaps you could try retrieving the boxArea.InnerHTML, appending the appropriate HTML string to it, and setting the boxArea.InnerHTML to the result.
Something like:
boxArea.InnerHTML = boxArea.InnerHTML + "<div class='largeBox'></div>";