How do I compare two jQuery objects for identity? - javascript

I'm trying to use jQuery to open / close control 'boxes' on a webpage. Unfortunately, it doesn't look very good to close a box just to re-open it if the user happens to click on the already opened box. (Boxes are mutually exclusive).
The code I'm using doesn't work, and I'm not sure why. I still get a box closing just to open up anew, which isn't the desired functionality. I created the 'val' variable for debugging purposes; in the debugger, it shows 'val' as having the exact same value as $(this), which should prevent it from getting to the .slideToggle() inside the if statement, but doesn't.
function openBox(index)
{
val = $('#box' + index);
$('.profilePageContentBox').each(function(){
if($(this).css('display') != 'none')
{
if($(this) != val)
{
$(this).slideToggle(200);
}
}
});
val.slideToggle(200);
}

You can also do:
if(val.is(this))

Using the $() function will always create a new object, so no matter what, your equality check there will always fail.
For example:
var div = document.getElementById('myDiv');
$(div) === $(div); // false!
Instead, you could try just storing the actual DOM elements, since those are just referred to inside jQuery objects.
val = $('#box'+index).get(0);
...
if (this !== val) { }

Try this:
function openBox(index)
{
val=$('#box'+index);
$('.profilePageContentBox').each(function(){
if($(this).is(":visible"))
{
if(!$(this).is("#box"+index))
$(this).slideToggle(200);
}
});
val.slideToggle(200);
}

Related

How to make Jquery's .html() function in Vanilla JavaScript

I've been looking all over the web for how to do this. I am trying to make Jquerys .html() function in vanilla JavaScript. I want to recreate so I can understand it better. I've tried the following but nothing seems to work, I just don't understand what I am doing wrong.
let $ = function(ele) {
if (ele.charAt(0) == '.' || ele.charAt(0) == '#') {
let newEle = cut(ele,0);
if (ele.charAt(0) == '#')
get(newEle);
else
return document.getElementsByClassName(newEle);
} else
return document.getElementById(ele);
}
$.prototype.html = function(html) {
this.innerHTML = html;
}
$('test').html('hey');
$('.hey')[0].html('hey');
function cut(string,num) {
let a = string.slice(0,num);
let b = string.slice(num + 1,string.length);
return a + b;
}
It doesn't work, the console log reports this error:
Uncaught TypeError: $(...).html is not a function
Please help and thanks in advance.
The problem here is what you are returning from the $ function.
Think about this: document.getElementsByClassName and document.getElementById return dom elements and dom elements don't have a .html function. That is why you are getting the error.
What you need is to return is an object, a wrapper, with a .html function, and a closure over the dom elements that you want to modify.
Because you're returning an object from $, you're overriding the default behavior of new $; instead of resulting in the newly-created object, it results in the object you returned out of $.
Instead, you'd want to remember the results of those getElementsByClassName/getElementById calls in an array or Set you store on this (the newly-created object), and then use that array or Set within html (since you want to loop over all matching elements in the case where you're setting the new HTML).
Side note: Since you're using ES2015+ features anyway (let), you might want to use the simpler class syntax instead of a separate function declaration and assigning to $.prototype:
class $ {
constructor(ele) {
// ...
}
html(html) {
// ..
}
}

Modify jQuery .prop();

Is any way to modify jQuery .prop() function? Now I'm using .trigger() but I's problematic for me because I have to use It in many places. Is any way to create something like global callback function to make It automatically?
My code now:
objCheckAll.prop('checked', true).trigger('pseudoInput.refresh');
I want to fire this event always when I'll use .prop('checked) like global behaviour.
how about turning it around: checking the property when you fire an event?
$(document).on('pseudoInput.refresh',function(event){
$(event.target).prop('checked',true);
});
$('input').trigger('pseudoInput.refresh');
another options is making a plugin for it that checks it and fires the event automatically
You could just overwrite jQuery's prop with your own
var oldProp = $.fn.prop;
$.fn.prop = function() {
oldProp.apply(this, arguments);
if (arguments[0] == 'checked') this.trigger('pseudoInput.refresh');
}
That would always call the trigger when prop('checked') is used
Based on #adeneo answer I made this code which is working for me
$.fn.oldProp = $.fn.prop;
$.fn.prop = function(a, b) {
if (a !== undefined && b !== undefined) {
this.oldProp(a, b);
if (a.indexOf('checked') > -1) {
this.trigger('pseudoInput.refresh');
}
return this;
} else if(a !== undefined){
return this.oldProp(a);
} else{
return this;
}
};
Here's my earlier suggestion which I think is a conceptually much better idea than changing the meaning of .prop().
jQuery.fn.checkAndTrigger = function() {
return this.prop('checked', true).trigger('pseudoInput.refresh');
};
Then, to use it you just do this:
objCheckAll.checkAndTrigger();
Based on your comments, if you're avoiding a conceptually better way to solve a problem because you don't want to use search and replace on some existing code, then you're either not very interested in righting good code or you really need to learn how to use your editing tools to make the search/replace pretty easy.

for each element of a class, check the contents to see if it contains a string, and then update only that element if it does

Code version 1:
var one = "one";
$.each($(".Buttons"),(function() {
if ($("this:contains(one)")) {
$(this).addClass( "currentButton" );
};
}));
Code version 2:
var one = "one";
$(".Buttons").each(function(a, e) {
if ($("e:contains(one)"))
{ $(e).addClass( "currentButton" ); };
});
I think you see what I'm trying to do. My problem is only updating the specific element where the text is matched, but all elements are updated when only one matches.
Edit: HTML below:
<input type="submit" class="Buttons" value="one">
<input type="submit" class="Buttons" value="two">
<input type="submit" class="Buttons" value="one & two">
I am using inputs as they are programmatically added buttons using asp.net/c#
I have attempted a couple of solutions and I'm still having every element have the class added.
I updated this jsfiddle with inputs and now it's not being affected at all. I'm guessing :contains won't check input value.
Using my original method I fixed it as follows, or alternatively see the answer from Elias:
$(".Buttons").each(function() {
if ($(this).attr("value") == one) {
$(this).addClass("currentButton");
};
});
The easiest way to do what you're doing is a one-liner, though:
var one = "one";
$(".Buttons").filter(":contains('"+one+"')").addClass("currentButton");
//or
$(".Buttons").filter(":contains('one')").addClass("currentButton");
check fiddle
This does imply your :contains selector is a constant value, if you need it to change according to somethign else, wrap it in a function:
function changeClass(contains)
{
contains = contains || 'one';//default value
$('.Buttons').filter(":contains('"+contains+"')").addClass('currentButton');
}
Of course, as always, you can change everything to parameters so this function is more likely to be reusable:
function changeClass(selector, filter, newClass)
{
$(selector).filter(filter).addClass(newClass);
}
changeClass('.Buttons', ':contains("one")', 'currentButton');
Your problems were being caused by enclosing either this or e inside the string delimiters, which effectively turned them into string constants, not references to the DOM nodes you were trying to change
What happened is that both:
if ($("e:contains(one)"))
{
$(e).addClass( "currentButton" );
}
and
if ($("this:contains(one)"))
{
$(this).addClass( "currentButton" );
}
evaluated to :
if ([])
{
$(this).addClass('currentButton');
}
In other words: you were passing string constants to the main jQuery function ($()) which probably tried to make the bet of things, and likely treated them as selectors. Sadly, they came up empty, so an empty array-like jQuery object was returned, and any object/array is a truthy value in JS, so the expressions checked evalueted to true, hence, nodes classes were changed.
You could just as well have written:
if ($('foobar, see if it matters what I write here'))
{
console.log('It doesn\'t');
}
And it'll log It doesn't time and time again.
Edit
In response to your comment, if what you actually want is to filter, based on the elements' value attribute:
$('.Buttons').filter(function()
{//this function will be applied to each element returned by the $('.Buttons') selector
//only those for which this callback returns true will get the currentButton class
return /\bone\b/i.test($(this).val());
//or, equally valid:
return ($(this).val().indexOf('one') !== -1);
}).addClass('currentButton');
Note that /\bone\b/i will accept "ONe" as well as "One" or "one", but won't return true if the value attribute contains "bones", whereas indexOf is CaseSensitive, but doesn't check if one is part of a word or not.
for a case sensitive regex, you can use /\bone\b/, without the i.
A more strict, but lot shorter version of the same thing could be:
$('.Buttons[value~="'+one+'"]').addClass("currentButton");
But there are so many jQ selectors that you can use, best keep a reference close to your chest
DEMO
Using is():
var one = "one";
$(".Buttons").each(function () {
if ($(this).is(":contains('"+one+"')")) {
$(this).addClass("currentButton");
};
});
You can do this pretty simply without an each function. jQuery selectors will do the filtering for you, and then you update every element that matched the selector.
var text = "text to test for";
$(".Buttons:contains(" + text + ")").addclass("currentButton");
Here is a jsFiddle to demonstrate.
$("e:contains(one)") here one is being considered as a string literal.
try like, "e:contains('"+one+"')" now one will be evaluated to your string.
var one = "one";
$(".Buttons").filter(function () {
return $(":contains('"+one+"')",this);
}).addClass("currentButton");
or
var one = "one";
$(".Buttons").filter(function () {
return this.innerHTML == 'one'; // note: innerHTML may be deprecated soon, avoid it
}).addClass("currentButton");
jsFiddle demo

How to check an element retrieved from DOM is still present in DOM

Let's say I got a DOM element, as a param of an event, for example click.
$(document).click(function() {
myElement = $(this);
});
How can I check later that myElement is still in the DOM?
I can't use .length or any other things like that because it still refer to the saved element and the state of the DOM at this moment, right?
You can check element parent:
function isInDom(obj) {
var root = obj.parents('html')[0]
return !!(root && root === document.documentElement);
}
if(isInDom(myElement)) {
...
}
Here's working fiddle: http://jsfiddle.net/vnxhQ/7/
You're probably looking for Node.isConnected.
The only reliable way I see so far is to check if the element is inside document.getElementsByTagName('*')
function isInDoc(element)
{
var elements=document.getElementsByTagName(element.tagName);
for(var i=0;i<elements.length;++i)
{
if(elements[i]===element)return true;
}
return false;
}
Demo: http://jsfiddle.net/doktormolle/hX8eN/
<edit>
Node.contains() seems to be supported by all major browsers, so I would finally suggest this:
if(document.documentElement.contains(myElement))
Demo: http://jsfiddle.net/doktormolle/LZUx3/
In order to test if an element still exists in the DOM, you need to crawl the DOM again:
// store it in some shared scope
var myElement;
$(document).click(function() {
myElement = $(this);
});
// sometime later...
if ($('#' + myElement.attr('id')).length > 0) {
// it still exists
} else {
// it no longer exists
}
Your clicked elements must all have ids for this to work, though. A class or any other selector could be used instead.
Edit: see this question for ideas on how to get a unique selector for any given DOM element.
This is assuming you would have the id of possible 'clickable' elements set
var idRef;
$(document).on('click', function() {
idRef = this.id;
});
later..
var exists = document.getElementById(idRef).length > 0;
You can the undocumented .selector property of a jQuery object to see if an element is still in the DOM, on the condition that it uses a unique ID. ​Obvious
http://jsfiddle.net/mblase75/CC2Vn/
$two = $('#two');
console.log($($two.selector).length); // 1
$two.remove();
console.log($($two.selector).length); // 0
See this question for more about how to get the selector of a jQuery object, which may or may not uniquely describe the DOM element(s) it contains.
Just to add something to the fray:
This is really Inferpse's answer slightly tweaked for Dr.Molle's corner case of creating another body element that might house the element removed from the general DOM tree (or, of course, maybe the element was never in the DOM in the first place.) Like Inferspe's answer, it takes a jQuery wrapped object, not the element itself.
function isInDom(jqobj) {
var someBody = jqobj.parents('body');
return someBody.length > 0 && someBody.get(0) === document.body;
}
​
I must admit I'm having trouble figuring out how I might try to break that.
Edit: Oh yeah... jsFiddle: http://jsfiddle.net/vnxhQ/5/
Edit II: of course, if we aren't talking about link or script elements (anything that might go into the head, not the body, I guess that might work fine :-/
Perhaps a better way of implementing Inferpse's function is by extending jQuery:
jQuery.fn.extend({
isInDom: function() {
var root = this.eq(0).parents('html')[0];
return !!(root && root === document.documentElement);
}
})
Usage:
$("div").isInDom() //returns true if your dom contains a div
$("<div />").isInDom() //returns false
$().isInDom() //returns false

Is it possible to get the event object for the "current" or "last" event, without receiving it as argument in the handler?

I'm trying to modify the behaviour or a JavaScript library, basically by monkeypatching it (no, there is no better way).
At a certain point in the code, I need to know whether Shift is pressed or not. If the event handler in this library were properly written, it'd receive the "event" as its first parameter, but unfortunately, it isn't (events are wired with onclick inline in the HTML)
So, I'm trying to see if jQuery "stores" the last event object somewhere, or if there is some other way to access it.
Essentially, what I want is "window.event", but ideally I'd like for it to work on Firefox.
Any ideas, besides adding a global onKeyDown handler to the document and keeping track of the state of Shift myself? That feels a bit overkill and a bit too global for my taste.
Can you wrap the function they are using as their event handler? Take this contrived example:
var someObject = {
keyDownListener: function() {
alert('something was pressed!');
}
}
We could replace keyDownListener with our own method that accepts the event object.
var someObject = {
keyDownListener: function() {
alert('something was pressed!');
}
}
var oldKeyDownListener = someObject.keyDownListener;
someObject.keyDownListener = function(event) {
oldKeyDownListener(); // Call the original
// Do anything we need to do here (or before we call the old one!)
}
If you can get inside the function, you can also inspect the arguments object. Your event object should be in there (the first item, I believe).
var f = function() {
console.log(arguments);
}
f(); //= Logs []
f(1); //= Logs [1]
f(1, 'something'); //= Logs [1, 'something']
EDIT (In response to the comment below).
If you can "hijack" the method, here's ONE way you could it. I'm not certain if this is a good approach but if you have all these constraints, it will get you what you want. Basically what this code does is it searches for any elements that have an onclick attribute on them and changes the method signature to include the event object.
We can then wrap the original listener to pull the event object out of arguments and then pass execution back to the original function.
Doing this will get you what you want (I think?). See this code:
HTML:
Click Me​
JavaScript:
window.myFunction = function(one, two, three) {
console.log("one: " + one + ", two: " + two + ", three: " + three);
}
var originalMyFunction = window.myFunction;
window.myFunction = function() {
var event = arguments[arguments.length - 1]; // The last item in the arguments array is our event object.
console.log(event);
originalMyFunction.apply(this, arguments);
}
$('[onclick]').each(function() {
var s = $(this).attr('onclick');
var lastIndex = s.lastIndexOf(')');
var s2 = s.substring(0, lastIndex);
var s3 = s.substring(lastIndex, s.length);
var s4 = s2 + ', event' + s3;
$(this).attr('onclick', s4);
});
You can see it working in this fiddle: http://jsfiddle.net/84KvV/
EDIT 2
If you wanna get really fancy with it, you could even automate the wrapping of the functions. See this fiddle: http://jsfiddle.net/84KvV/2/.
Please note that this is expecting the function strings to be in a certain format so that it can parse it (functionName(...)). If it's not that in that format, this exact code will not work :)
As mentioned in my comment, it IS possible to make window.event exist in browsers that are not IE. It requires you to wrap attachEvent/addEventListener. It would go something like this:
var oldAddEventListener = HTMLElement.prototype.addEventListener;
HTMLElement.prototype.addEventListener = function(type, listener, useCapture) {
var wrapped = function(event) {
window.event = event;
listener(arguments);
}
oldAddEventListener.call(this, type, wrapped , useCapture);
}
As I said, I'm not sure if this will work for inline event listeners, but it might :) If not, at least it's here as a reference for those looking for something similar.
function eventlessHandler(myVal) {
var event = window.event || eventlessHandler.caller.arguments[0];
// some work
}
It may be needed traverse .caller several times depending on actual call chain.
Tested this on IE6, IE11 and latest Chrome. Should work on most other browsers too.
See also How do you find out the caller function in JavaScript?.

Categories