4

I wish I could write something like this

$("div.banana .for aside.scale").yellowify();

$.fn.yellowify = function () {
    this.css("color","yellow");
    alert($(this).selector);
});

which would alert

div.banana .for aside.scale

The question is very similar to this one, however, there the poster wants to retrieve a DOM structure by scanning parents of the selector or something like that. I simply want the selector as is, without any additional classes or IDs. I want it exactly as it's there in the first line.

jQuery used to have this .selector property, but it has been deprecated in 1.7 and removed in 1.9 (or has it? - thanks Ted. UPDATE .selector will only be removed in jQuery 3.0!). The docs state:

Plugins that need to use a selector string within their plugin can require it as a parameter of the method. For example, a "foo" plugin could be written as $.fn.foo = function( selector, options ) { /* plugin code goes here */ };, and the person using the plugin would write $( "div.bar" ).foo( "div.bar", {dog: "bark"} ); with the "div.bar" selector repeated as the first argument of .foo().

But I think that that's incredibly ugly and not user-friendly at all. Is there an alternative possible that doesn't require any action on the user's side? I.e. without needing any additional arguments?


For those who are curious, ideally I want my plugin to be able to echo styles for the specific element, something like this, but obviously much more complicated:

$.fn.yellowify = function () {
    var css = $(this).selector + "{color: yellow;}";
    // I can add rules for specific elements now as well
    css += $(this).selector + " .child {color: red;}";
    $("<style>").html(css).appendTo("head");
});

Together with arguments, this allows me to have a far reach with my plugin.

Community
  • 1
  • 1
Bram Vanroy
  • 27,032
  • 24
  • 137
  • 239
  • 2
    Your code actually works as is...with jQuery 1.11.0... [check out this fiddle](http://jsfiddle.net/g6mkv7fc/). Dunno about it being deprecated, it's still there. – Ted May 27 '15 at 19:16
  • @Ted That's strange, I just wrote some code by heart. It's even ven stranger that it isn't removed. [The docs](https://api.jquery.com/selector/) clearly state that it ought to be removed since 1.9. But [even in the 2.x branch](http://jsfiddle.net/BramVanroy/vz3xc3se/1/) it still seems to be working. – Bram Vanroy May 27 '15 at 19:20
  • Yeah, I just checked the newer versions in that fiddle and it runs on all except for edge – Ted May 27 '15 at 19:21
  • @Ted The edge version of jQuery has many bugs, so I wouldn't be bothered by that. – Bram Vanroy May 27 '15 at 19:22
  • 1
    What is the use case for it. `$(this)` lets you work with the result you would normally get with `$('div.bar')` perhaps you want `$.yellowify('div.foo')` instead? – Sukima May 27 '15 at 19:23
  • Why exactly do you need to get the selector? generally the solution to this problem is to not rely on needing the selector. If you're using it for event delegation, you're doing it wrong. – Kevin B May 27 '15 at 19:25
  • It does get lost in dom traversal though (like with `.parent()`). Regardless, it's an interesting little feature I was unaware of :) – Ted May 27 '15 at 19:25
  • @Sukima See my edit. – Bram Vanroy May 27 '15 at 19:30
  • @KevinB See my edit. – Bram Vanroy May 27 '15 at 19:30
  • You should use Sukima's suggestion instead. – Kevin B May 27 '15 at 19:36
  • 2
    Note that jQuery selectors and CSS selectors is not the same. There are some selectors that works in jQuery that won't work in the CSS. – Guffa May 27 '15 at 19:36
  • There's also some that will work in CSS but not jquery (though i guess that's less important to your usecase) – Kevin B May 27 '15 at 19:37
  • @KevinB Why's that? Don't plugins normally run in my way, rather than his? What's the difference? In my actual case I am running a plugin on a selector and a user can add arguments to it. – Bram Vanroy May 27 '15 at 19:38
  • @Guffa Could you give an example of a non-chained selector that can't be used in CSS? I'm curious! (no sarcasm) – Bram Vanroy May 27 '15 at 19:38
  • @BramVanroy His way doesn't actually select elements, and instead will give you the selector directly. You can then select the elements if you need to. And, no, not all plugins are built like yours. – Kevin B May 27 '15 at 19:39
  • @BramVanroy `:has()`, `:gt()`, `:lt()` etc... – A. Wolff May 27 '15 at 19:48
  • @A.Wolff D'oh, again. Obviously, you're right. I'm running into too many "d'oh"'s today. I need some time off. – Bram Vanroy May 27 '15 at 19:49
  • To all: **UPDATE** `.selector` will [be removed in jQuery 3.0](https://github.com/jquery/jquery/issues/2355#issuecomment-106043451!) Apparently, it was a mistake on the developers' side. Note though that it's still *deprecated* and usage for new projects is not advised. – Bram Vanroy May 27 '15 at 19:51

1 Answers1

0

In your example your use case is a static plugin example not a prototype plugin example. Using $.fn.myPlugin your asking the user of your plugin to manipulate it as it it was part of the jQuery API. In that case the plugin is designed to iterate over the jQuery object at that point in the call chain ($(this).each()).

However your example looks lke it acts on the actual jQuery object itself not on selected elements adn would be better served as a plugin on the jQuery class object:

$.yellowify = function (selector) {
  var css = selector + "{color: yellow;}";
  // I can add rules for specific elements now as well
  css += selector + " .child {color: red;}";
  $("<style>").html(css).appendTo("head");
});

It isn't entirely clear why you feel you want to do this. It's strange an alien. However, for completness plugins that act on the jQuery prototype chain need to loop over the already selected elements and return the original collection. If you need to look for elements within the selection then you would use .find().

Sukima
  • 9,965
  • 3
  • 46
  • 60
  • This is the very first plugin I am writing, so I am new to all this terminology. I don't expect you to explain the difference between a static plugin and a prototype plugin, but could you provide me with some reading material? I'm afraid I don't quite grasp the difference between the two. – Bram Vanroy May 27 '15 at 19:50
  • 1
    @BramVanroy http://stackoverflow.com/questions/2845981/jquery-difference-between-functionname-and-fn-functionname The bottom of the selected answer has even more links, if you need further reading. – Kevin B May 27 '15 at 19:53
  • @KevinB Thanks for that. Now I understand the confusion. My actual plugin is much more thorough than `yellowify` - which I made up for illustrative purposes. In my actual plugin, I do need to use the `$.fn.*` form because I manipulate the elements as well as add that stylesheet. – Bram Vanroy May 27 '15 at 19:58
  • I don't know your use case but the idea of **adding a style tag** feels like a code smell to me. Is there a better way? – Sukima May 27 '15 at 19:59
  • There isn,'t unfortunately. The reason for this is, that to prevent polluting the DOM, I create elements and then remove them after manipulating them (we're talking potentially hundreds of elements that are created on click). These elements need some values for the CSS property `animation`, but these values are dependent on some posteriori data. Additionally, the `animation` has to start as soon as the element is created. Therefore, it seemed better for me to *once* create a style tag than keep polluting and spamming the DOM with hundreds of identical inline styles. – Bram Vanroy May 27 '15 at 20:03
  • 1
    Note that you don't *need* to use `$.fn` to get the functionality you are looking for. You can still select the elements using the `$.method` style. I still suggest using the `$.method` style if you need the selector, otherwise your documentation will be bloated with explaining to the end user that they can't do `$(selector).find('foo').yellowify()` – Kevin B May 27 '15 at 20:05
  • @BramVanroy adding a style rule per _selector_ is just as bad. Just add a plugin specifix class to the selected elements in your plugin and then tell the user (or use a static method to add the CSS) to base the style off that one rule. – Sukima May 27 '15 at 20:26
  • @Sukima As I said, I can't do that. For instance, end-user can decide animation speed in a variable in JS, which will in the end need to output as `animation: someFunc 700ms` in which 700ms is the specified duration. As I explained above, it would seem bad practise to apply this style to *every* element on creation. – Bram Vanroy May 27 '15 at 20:29
  • 1
    @KevinB As a side note, `$(selector).find('foo')` would work because from source: `// Needed because $( selector, context ) becomes $( context ).find( selector ) ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); ret.selector = this.selector ? this.selector + " " + selector : selector;` But not `children()`, `siblings()`, `prevAll()`, etc... ;) – A. Wolff May 27 '15 at 20:31
  • Example on how to manage this CSS problem: http://jsbin.com/vefurelija/1/edit?js,output – Sukima May 27 '15 at 20:32
  • 1
    I think this comment thread has finally devolved into an [XY problem](http://xyproblem.info/). – Sukima May 27 '15 at 20:33
  • @Sukima As I said, I can't do that. I'll just give you the actual project so you can see why not. Though I do understand now that it would be better to use `$.plugin` in my case. Here is the [project](http://jsfiddle.net/BramVanroy/afw7fumu/). – Bram Vanroy May 28 '15 at 09:35