どんな講釈をヌかそうと、要らないものは要らない。
・メルカリが検索結果に「売れた商品」も表示するのはなぜ? 商品検索におけるUI/UXの考え方 - ログミーTech
https://logmi.jp/tech/articles/321231
邪魔だから消してみると何が起きるかと言うと、全KPIがすべて下がります。並みの勢いじゃないですよ。出品転換率、つまり1人のお客様が出品行動を取ったという行動も減るし、買ったという行動も減ります。
その結果、結局取引総数も取引総額もすべて下がって、負のスパイラルに入る。それで慌てて元に戻すというのを、実はUSとJPを合わせて、4~5回は繰り返しているんじゃないかと思います(笑)。
いったい、なぜなのか? 長らく謎でした。ちなみに創業者の山田進太郎は「ユーザーインタビューとかで、邪魔だからと言われているので消してもいいですよね?」と言われたとき、「いや、俺はあったほうがいいと思うけどね」と言ったそうです。
「なんか知らないけど、あったほうがいいと思う」と言ったと。3年前くらいですね。直感的にわかっている感じでした、あの人は(笑)。予言していたんです。
僕は「こういうことなんだろうな」という仮説があるんです。検索改善をしていて、僕がうまくいったときというのは、必ずさっきの購買転換率が上がるんです。
「やった! 検索結果がよくなって統計上有意に買う人が増えた! 買う数が増えた!」と思うと同時に、出品する人も増えるんですね。「なんで検索改善しているのに出品する人が増えるんだ? 俺は出品を増やそうとはしていないんだけど」と思っていました。
結局、行き着いた答えが「出品しようとしたときに検索してるんだ」ということです。つまり、買おうとする人もいるし、売ろうとする人もいる。Nintendo Switchを持っている人が売ろうとしたときに、「Nintendo Switch」と検索して何を見ているかという話ですね。
転売ヤーの気持ちなんか知るかヴォケ。
真っ当な購買には最低のUXですね。
環境
- Tampermonkey
- メルカリの検索結果
User Script
案① 検索結果から売り切れを削除する.
// ==UserScript==
// @name 案① 検索結果から売り切れを削除する.
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://www.mercari.com/jp/search/?*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Your code here...
document.querySelectorAll('div.items-box-content section').forEach(function(section) {
if(section.querySelectorAll('div.item-sold-out-badge').length > 0) {
section.parentNode.removeChild(section);
}
});
})();
安心感が強い半面、詳細検索と共存出来ない残念さがある。
div.item-sold-out-badge
のある section
を削除する。
案② 画面移動
パラメータが keyword
のみの場合、画面上部からの検索だと判定し、その場合には 販売中
の検索条件を追加した場合のURLに移動する。
// ==UserScript==
// @name 案② 画面移動.
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://www.mercari.com/jp/search/?*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Your code here...
let querys = [...new URLSearchParams(window.location.search).entries()].reduce(function(obj, e) {return {...obj, [e[0]]: e[1]}}, {});
console.info(Object.keys(querys).length);
if(Object.keys(querys).length == 1) {
if(querys.keyword) {
location.replace(location.href + '&status_on_sale=1');
}
}
})();
- before
https://www.mercari.com/jp/search/?keyword=PS5 - after
https://www.mercari.com/jp/search/?keyword=PS5&status_on_sale=1
JavaScriptでQuery Stringを解釈する楽しさはあるが、全体的にはナンセンス。
案③ デフォルトの検索条件に付与 (1shot)
input
の type
が search
のものを画面上部の検索ボックスとして扱い、同 form
中に 販売中
の検索条件のための hidden
を追加する。
// ==UserScript==
// @name 案③ デフォルトの検索条件に付与 (1shot)
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://www.mercari.com/*
// ==/UserScript==
(function() {
'use strict';
// Your code here...
setTimeout(function() {
document.querySelectorAll('input[type="search"]').forEach(function(e) {
let p = e.parentNode;
let div = document.createElement('div');
let f = function (key, val) {
let input = document.createElement('input');
input.type = 'hidden';
input.name = key;
input.value = val;
return input;
};
div.appendChild(f('status_on_sale', '1'));
p.removeChild(e);
div.appendChild(e);
p.prepend(div);
});
}, 1);
})();
読み込み後の謎な処理で、form内のエレメントが2つに戻されるクソみたいな挙動があるので。独自タグでwarpして退避している。たまにうまく反応してくれないときもあるので要調整。
案④ デフォルトの検索条件に付与 (observer)
input
の type
が search
のものを画面上部の検索ボックスとして扱い、同 form
中に 販売中
の検索条件のための hidden
を追加する。
案③だと、たまに戻されてしまうので、親ノードを監視して再設定させる。 (多重で付かない様に、無かったら追加する。)
// ==UserScript==
// @name 案④ デフォルトの検索条件に付与 (observer)
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://www.mercari.com/*
// ==/UserScript==
(function() {
'use strict';
// Your code here...
let f = function(p) {
let div = document.createElement('div');
let a = function(name, val) {
if(p.querySelectorAll('input[name="' + name + '"]').length == 0) {
let input = document.createElement('input');
input.type = 'hidden';
input.name = name;
input.value = val;
p.appendChild(input);
}
}
a('status_on_sale', '1');
}
document.querySelectorAll('input[type="search').forEach(function(e) {
let p = e.parentNode;
// first
(function() {
f(p);
})();
// observer
(function() {
let observe;
let observer = new MutationObserver(function() {
observer.disconnect();
f(p);
observe();
});
observe = function() {
observer.observe(p, {
childList:true,
});
}
observe();
})();
});
})();
案③のformエレメントに追加しても削除されてしまう問題について、observerで潰した版。
詳細検索と衝突せず、TOP並びに検索画面に埋め込めた。一番美しい気がする。
以上。
参考 (謝辞)
- JavaScriptのMutationObserverでDOMの変化を監視する方法 - Qiita
https://qiita.com/munieru_jp/items/a6f1433652124a2165e4