Javascript-форум (https://javascript.ru/forum/)
-   Элементы интерфейса (https://javascript.ru/forum/dom-window/)
-   -   Цепочка вызовов jQuery (https://javascript.ru/forum/dom-window/51556-cepochka-vyzovov-jquery.html)

OlegALL 11.11.2014 09:33

Цепочка вызовов jQuery
 
Привет

Задача - сделать так: $('div').addClass('class1').html().addClass('class 2').html(); (без new), не создавая экземпляра класса, оставив при этом прототипное наследование.



function $(selector){
			this.tags = document.querySelectorAll(selector);
		}
		
		$.prototype.addClass = function(className){
			for (var i = 0; i < this.tags.length; i++){
				this.tags[i].classList.add(className);
			}
			return this;			
		}
		
		$.prototype.html = function(){
			this.tags[0].innerHTML;
			console.log(this.tags[0].innerHTML);
			return this;
		}		
		
		new $('div').addClass('class1').html().addClass('class2').html();

Erolast 11.11.2014 10:38

https://ru.wikipedia.org/wiki/Fluent_interface

ksa 11.11.2014 10:39

Цитата:

Сообщение от OlegALL
Задача - сделать так:
$('div').addClass('class1').html().addClass('class  2').html();

Ответ - "так" не сделать...

OlegALL, ты, что вообще хочешь получить?
Присвоить ДИВу два класса и вернуть его ХТМЛ?

OlegALL 11.11.2014 10:54

Цитата:

Сообщение от ksa (Сообщение 340363)
Ответ - "так" не сделать...

OlegALL, ты, что вообще хочешь получить?
Присвоить ДИВу два класса и вернуть его ХТМЛ?

Да

ksa 11.11.2014 11:11

Цитата:

Сообщение от OlegALL
Да

Так и пиши...

var val=$('div').addClass('class1 class2').html();

OlegALL 11.11.2014 11:29

Спасибо Erolast за ссылку. Получилось так:

var $ = function (selector){
			this.tags = document.querySelectorAll(selector);
			if(this.$) {
				return new $(selector);
			}
		}
		
		$.prototype.addClass = function(className){
			for (var i = 0; i < this.tags.length; i++){
				this.tags[i].classList.add(className);
			}
			return this;
		}
		
		$.prototype.html = function(){
			this.tags[0].innerHTML;
			console.log(this.tags[0].innerHTML);
			return this;
		}

		
		$('div').addClass('class1').html().addClass('class2').html();


Только вопрос: почему зацикливается вызов $, если писать
так:
return new $(selector);

а не так:
if(this.$) {
    return new $(selector);
}

krutoy 11.11.2014 11:59

Цитата:

Сообщение от OlegALL
var $ = function (selector){            this.tags = document.querySelectorAll(selector);             if(this.$) {                return new $(selector);            }      � � }

Толком не вникал, но беглый взгляд -- и уже детская ошибка.
<html>
    <head>
    </head>
 
    <body>

<div>foo</div>
<div>bar</div>
<div>baz</div>

       <script>

var $ = function (selector){
            this.tags = document.querySelectorAll(selector);
            if(this.$) {
                return new $(selector);
            }
        }
$("div")
alert(tags)
        </script>
    </body>
 
</html>

danik.js 11.11.2014 12:30

Цитата:

Сообщение от krutoy
if(this.$) {

Кто тебя надоумил такое написать?

Логика такая: если this - инстанс конструктора $, то все пучком, делаем поиск по селектору.
Если же нет - создаем инстанс сами через new и возвращаем результат.

Таким образом, когда будешь писать $('div'), то будет два вызова - один как вызов функции, второй - как вызов конструктора (через new, создание инстанса то есть). Оно у тебя и щас так работает, но условие не совсем подходящее.

krutoy 11.11.2014 12:41

Цитата:

Сообщение от danik.js
Кто тебя надоумил такое написать?

Это не я написал, это код топикстартера. Я, собственно, просто продемонстрировал, что его ф-ция срет в глобал, дальше я не смотрел:)

krutoy 11.11.2014 12:45

danik.js,
Слушай, по-моему это чушь какая-то. Какие нах*й инстансы и классы? цель банальна -- вернуть (псевдо) объект, так? Так почему бы не написать тупо
proto={}
$=function(sel){
   var o = document.querySelectorAll(sel)
   o.__proto__=proto
   return o
}

proto.addClass = function(className){
   for (var i = 0; i < this.length; i++){
           this[i].setAttribute("class", className)
   }
           return this;
}

и так далее. Зачем этот цирк?

krutoy 11.11.2014 13:07

danik.js,
можно даже так, чтоб лишние имена в глобале не плодить:
<html>
    <head>
<style>
.foo{background: red}
</style>
    </head>
 
    <body>

<div>foo</div>
<div>bar</div>
<div>baz</div>

       <script>

$=function(sel){
   var o = document.querySelectorAll(sel)
   o.__proto__=$
   return o
}

$.addClass = function(className){
   for (var i = 0; i < this.length; i++){
           this[i].setAttribute("class", className)
   }
           return this;
}

$("div").addClass("foo")

        </script>
    </body>
 
</html>

OlegALL 11.11.2014 13:53

спасибо, вариант krutoy - наиболее удачный

Erolast 11.11.2014 14:55

Цитата:

Я, собственно, просто продемонстрировал, что его ф-ция срет в глобал, дальше я не смотрел
Сам же вечно срешь.
Цитата:

наиболее удачный
И наиболее неправильный.

krutoy 11.11.2014 16:12

OlegALL,
Прошу прощения, чуть поспешил. В ФФ не работает, может еще где. Вот этот код протестировал в ФФ Хроме и старой опере. В IE не тестил.
<html>
    <head>
<style>
.foo{background: red}
</style>
    </head>
  
    <body>
 
<div>foo</div>
<div>bar</div>
<div>baz</div>
 
       <script>

 
$=function(sel){
   var extend=function(src, trg){
      for(var i in src) if(!isNaN(i)) trg[i]=src[i]
   }
   var o = Object.create($)
   extend(document.querySelectorAll(sel), o)
   console.log(o)
   return o
}

 
$.addClass = function(className){

     for(var i in this){
         if(this[i].setAttribute) this[i].setAttribute("class", className)
     }
      return this;
}
 
   $("div").addClass("foo")
        </script>
    </body>
  
</html>

Но гарантировать нгичего не могу, сам экспериментирую:)
Это все из-за идиотского поведения нативного API, этих гребаных коллекций, которые и объекты, сука, и не объекты.

krutoy 11.11.2014 16:17

Цитата:

Сообщение от Erolast
Сам же вечно срешь.

Ты можешь отличить, когда глобал засираетсмя неявно, и когда прогер сам объявляет переменные в глобале?
Цитата:

Сообщение от Erolast
И наиболее неправильный.

Первая версия была неправильной, да.
Интересно было бы на твою версию взглянуть. Или опять сольешься?

Erolast 11.11.2014 18:04

Цитата:

Ты можешь отличить, когда глобал засираетсмя неявно, и когда прогер сам объявляет переменные в глобале?
Могу. Ты как раз засираешь неявно.
Цитата:

Интересно было бы на твою версию взглянуть.
Легко. Но не для тебя.
Так на ES6:
class $ {
  constructor(selector) {
    if (!(this instanceof $))
      return new $(selector);

    Object.assign(this, document.querySelectorAll(selector));
  }

  addClass(className) {
    for (let element of Array.from(this)) {
      element.classList.add(className);
    }
    return this;
  }
}

Так на ES5:
function $(selector) {
  if (!(this instanceof $))
    return new $(selector);

  var queryResult = document.querySelectorAll(selector);
  for (var i = 0; i < queryResult.length; i++) {
    this[i] = queryResult[i];
  }
  this.length = queryResult.length;
}

$.prototype.addClass = function(className) {
  for (var i = 0; i < this.length; i++) {
    var element = this[i];
    if (~element.className.indexOf(className))
      continue;
    else
      element.className += (" " + className);
  }
  return this;
}

krutoy 11.11.2014 19:06

Цитата:

Сообщение от Erolast
Могу. Ты как раз засираешь неявно.

Где я это делал? ткни пальцем или обосрись.
Цитата:

Сообщение от Erolast
Так на ES6

Засунь его себе в жопу.
Цитата:

Сообщение от Erolast
Так на ES5:

Так это же то же самое почти что я написал в последней версии, синтаксис только другой. Но я верю, что ты этого не понял, потому что, то, что я пишу -- это из головы, я думаю, а потом делаю, иногда ошибаюсь, а все что ты постишь тут -- тупая, слегка переделанная паста чужого кода. В частности, объясни, мягинький, зачем ты написал вот эту чушь:
Цитата:

Сообщение от Erolast
if (!(this instanceof $)) return new $(selector);

если можно было просто создать объект и явно им манипулировать? В данном случае, это не нужно, но ты тупо перенес шаблон сюда, потому что ты не понимаешь, что там происходит, и, посему, подправить код не можешь.
Расскажи, чипушила, чем твоя $ отличается от этой
function $(selector) {
  var o=Object.create($.prototype),
  queryResult = document.querySelectorAll(selector);
  for (var i = 0; i < queryResult.length; i++) {
    o[i] = queryResult[i];
  }
  o.length = queryResult.length; return o
}

посмеши пацанов.
Ты делаешь финты ушами только для того чтобы создать объект? Lol.

Erolast 12.11.2014 04:35

Цитата:

Где я это делал?
Цитата:

function $(selector) {
  var o=Object.create($.prototype),
  *!*queryResult = document.querySelectorAll(selector);*/!*
  for (var i = 0; i < queryResult.length; i++) {
    o[i] = queryResult[i];
  }
  o.length = queryResult.length; return o
}

Например.

danik.js 12.11.2014 06:28

Erolast, на конец предыдущей строки посмотри.

OlegALL 12.11.2014 09:56

Сейчас другой вопрос.
<body>
	<div class="class0"> <a href="">link</a> </div>
	<div class="class0"></div>
	<div class="class0"></div>
</body>

proto={};
		
		$ = function(selector){
			var tags = document.querySelectorAll(selector);
			tags.__proto__ = proto;
			return tags;
		}

		proto.addClass = function(className){
			for (var i = 0; i < this.length; i++){
			    this[i].classList.add(className);
		    }
			return this;
		}

		proto.html = function(){
			return this[0].innerHTML;
		}


console.log( $('div').addClass('class1').html() ); так работает
console.log( $('div').addClass('class1').html().addClass('class 2').html() ); а так нет.
html() с одной стороны должен возвращать this[0].innerHTML, а с другой this. Как это объединить не знаю. На несовершенство кода пока обращать внимания не надо. как-то надо использовать toString() / valueOf()

Erolast 12.11.2014 10:18

Цитата:

Erolast, на конец предыдущей строки посмотри.
А. Ну так надо нормально форматировать.
Энивей, он в глобале var не использует, что привносит неочевидности - создается впечатление, что эта переменная уже была где-то объявленна (не говоря уже о том, что не во всех имплементациях внешний код в выполняется в глобале).
Цитата:

html() с одной стороны должен возвращать this[0].innerHTML, а с другой this. Как это объединить не знаю.
Никак. html должен возвращать this[0].innerHTML - здесь текучий интерфейс прерывается.

krutoy 12.11.2014 11:12

Цитата:

Сообщение от Erolast
не говоря уже о том, что не во всех имплементациях внешний код в выполняется в глобале

Оппа, да ты виднейший представитель CS. У тебя своя теория выполнения кода. Ну поясни тогда, что такое "внешний код" и "выполнение в глобале", раз уж кукарекнул. Это, наверное, когда в компьютере сидит какой-то дядя, получает инструкции, переносит их в "глобал", а потом выполняет, да, сладенький? А в некоторых имплементациях, он говорит: "фи, я буду выполнять код в локале, или в Анале". Ты знаком с термином "область видимости переменных"? Вот если хочешь какого-то конструктива, пляши от этого в своих высерах. Похоже, цирк маст гоу он.

krutoy 12.11.2014 11:25

Erolast,
И, кстати, насчет твоего предыдущего высера по поводу явного/неявного. Если я изнутри функции меняю глобальную переменную, которая еще не объявлена, это не значит, идиот, что это делается неявно. На самом деле, я меняю перемнную со значением undefined на переменную со значением some_value, семантически. Неявно -- это когда ты не понимаешь, что ты делаешь, примерно как ты. Если ты по тупости своей, непреднамеренно изменил переменную в глобале, или создал, вот это есть неявно. В остальных случаях -- все явно, идиот.
local={}
;(function(){this.x=1}).apply(window)
;(function(){this.x=1}).apply(local)
alert([window.x, local.x])

Задумайся, ущерб, почему бы тебе не объявлять в local переменные через вар, lol. Это тебе, блять, не паскаль, здесь семантика massage passing. Вот из=-за таких уродов как ты, отрыжек дейксры, нормальный язык изуродовали до неузнаваемости.

OlegALL 27.11.2014 11:39

Продолжаем решение поиска ответа:
Нужно, чтобы цепочка не разрывалась

Код:

var $ = function (selector){
			this.tags = document.querySelectorAll(selector);
			if (this.$) {
				return new $(selector);
			}
		}
		
		$.prototype.addClass = function(className){
			for (var i = 0; i < this.tags.length; i++){
				this.tags[i].classList.add(className);
			}
			return this;
		}
		
		$.prototype.html = function(){
			return this.tags[0].innerHTML;
		}

console.log($('div').addClass('class1').html().addClass('class2')); // нужно, чтобы работало


комментарий от специалиста, который поставил задачу:
"Надо переопределить toString у this внутри методов.
Теперь если результат ты используешь как строку,
она у тебя и будет строкой, хотя возвращать ты будешь this"

Попробовал, не работает. И вообще, не знаю как это сделать. Даже не совсем понял комментарий. Помогите

Erolast 27.11.2014 12:00

Да с чего ты вообще взял, что это должно работать? html возвращает данные, он прерывает текучий интерфейс и ставится последним в цепочке вызовов. Какова, по-твоему, его функция при расположении в середине? Красоты ради?

danik.js 27.11.2014 12:01

Ну тогда надо не this возвращать, а новый объект. Иначе такой код не будет работать:
var  $div = $('div');

var html = $div.html(); // меняет toString на выдачу html

var text = $div.text(); // меняет toString на выдачу text

alert(html); // выдаст text вместо html



Но тогда html !== $div, но по-другому и быть не может, иначе возникает противоречие.

Erolast 27.11.2014 12:10

Да это просто не нужно. html в любом случае будет в конце цепочки распологаться, иначе он не вернет данные == не нужен.

danik.js 27.11.2014 12:13

Кстати, html возвращает клон, и он тащится далее по цепочке. Таким образом $().html().addClass().toString() вернет html, а не [object Object]
Значит, нужно в каждом методе создавать клон. Короче, херня получается.
<div>This is <b>content</b></div>
<script>
var $ = function (selector){
    this.tags = document.querySelectorAll(selector);
    if (this.$) {
        return new $(selector);
    }
}
 
$.prototype.addClass = function(className){
    for (var i = 0; i < this.tags.length; i++){
        this.tags[i].classList.add(className);
    }
    return this;
}
 
$.prototype.html = function(){
    var clone = new $('_');
    clone.tags = this.tags;
    clone.toString = function() {
        return this.tags[0].innerHTML;
    };
    return clone;
}

var $div = $('div').addClass('class1').html().addClass('class2');
console.log($div); // нужно, чтобы работало

alert($div.html());
</script>

OlegALL 27.11.2014 14:48

Спасибо!
Сейчас 3 вопроса:
1.
так: alert($('div').addClass('class1').html().addClass( 'class2').html()); - всё гуд

а так:
console.log($('div').addClass('class1').html().add Class('class2').html()); - выводит this ($ {tags: NodeList[3], toString: function, addClass: function, html: function})

почему?

2. Объясните смысл
if (this.$) {
	return new $(selector);
}

Здесь нужно переделать. Комментарий специалиста:
"if (this.$) - плохое решение. Лучше проверять instanceof."
Мне пока непонятно

3.
почему в клоне нет проверки
if (this.$) {
	return new $(selector);
}

И можно ли сделать наследование от $? или фактически оно и есть?

UPD.

Текущий код:
var $ = function (selector){
			this.tags = document.querySelectorAll(selector);
			if (this instanceof window.constructor) {
				return new $(selector);
			}
		}
		
		$.prototype.addClass = function(className){
			for (var i = 0; i < this.tags.length; i++){
				this.tags[i].classList.add(className);
			}
			return this;
		}

		$.prototype.html = function(){
			var clone = new $('_');
			clone.tags = this.tags;
			clone.toString = function() {
				return this.tags[0].innerHTML;
			};
			return clone;
		}


Правильно ли сделано это:
if (this instanceof window.constructor) 
{
	return new $(selector);
}


Часовой пояс GMT +3, время: 23:13.